Thank you for contributing to the TrustLink backend! This service is the automation layer that bridges the physical world and the blockchain β watching for on-chain events, tracking shipments, triggering fund releases, and keeping buyers and vendors informed in real time.
Your contributions keep the oracle running cleanly and securely.
- Code of Conduct
- Stellar Wave Program
- Before You Start
- Development Setup
- Project Structure
- Making Changes
- Coding Standards
- Commit Convention
- Pull Request Process
- Testing
- Database Migrations
- Security Vulnerabilities
- Getting Help
This is a welcoming project. Be constructive, be patient with newcomers, and assume good intent. We're building financial infrastructure for social commerce in underserved markets β the stakes are real and the community reflects that seriousness.
Harassment, dismissiveness, or toxic behaviour will not be tolerated.
This repository participates in the Stellar Wave Program β a sprint-based contribution program funded by the Stellar Development Foundation where developers earn real XLM rewards for resolving open issues.
- Browse
Stellar Waveandgood first issueissues - Sign in at drips.network/wave with GitHub
- Apply to the issue with a brief note on your approach
- Get assigned β build β open a PR before the Wave cycle closes
- Merged PR = Points = XLM rewards
| Label | Points | Scope |
|---|---|---|
complexity: trivial |
100 pts | Swagger docs, missing validation, small bug |
complexity: medium |
150 pts | New endpoint, new service method, integration test |
complexity: high |
200 pts | New module, background worker, external API integration |
β‘ Apply before the Wave sprint is fully subscribed. Maintainers move quickly during active cycles.
Before contributing, read SPECIFICATION.md β it is the authoritative specification for this project. Every code change must conform to its requirements. PRs that violate the specification will be rejected.
Key requirements from the SRD:
- No
anytypes β strict TypeScript required - Tests required for all new code β coverage must not drop below 70%
- Security first β JWT signature verification, input sanitization, PII encryption
- CI must pass β lint, typecheck, test, build, audit all required
- Conventional commits β
type(scope): description - Repository pattern β services never call Prisma directly
- New to NestJS or Node.js? β Start with
good first issue. These have full context and don't require deep Stellar knowledge. - Backend-experienced? β Look for
complexity: mediumorcomplexity: highissues. - Have an idea? β Open a GitHub Discussion before writing code. An upfront conversation prevents your PR from being closed due to design misalignment.
- Scan the issue thread for maintainer comments β the scope may have changed since it was filed.
- Check for an existing open PR on the issue β don't duplicate effort.
- For anything that touches the Stellar transaction signing flow, the auto-release worker, or the dispute resolution logic β leave a comment describing your proposed approach and wait for a maintainer thumbs-up before starting.
| Tool | Version | Notes |
|---|---|---|
| Node.js | 20+ |
Use nvm |
| npm / pnpm | latest | |
| PostgreSQL | 15+ |
Local install or Docker |
| Docker | optional | For spinning up Postgres quickly |
# 1. Fork and clone
git clone https://github.com/YOUR_USERNAME/trustlink-backend
cd trustlink-backend
# 2. Add upstream
git remote add upstream https://github.com/your-org/trustlink-backend
# 3. Install dependencies
npm install
# 4. Set up environment
cp .env.example .env
# Edit .env β testnet values are pre-filled, you need your own DB URL
# 5. Start Postgres (Docker shortcut)
docker run --name trustlink-pg \
-e POSTGRES_USER=trustlink \
-e POSTGRES_PASSWORD=trustlink \
-e POSTGRES_DB=trustlink_dev \
-p 5432:5432 -d postgres:15
# 6. Run migrations
npx prisma migrate dev
# 7. Seed the database (optional β creates test escrow records)
npx prisma db seed
# 8. Start the dev server
npm run start:devThe API will be available at http://localhost:3001.
Swagger docs: http://localhost:3001/api/docs
git fetch upstream
git rebase upstream/maintrustlink-backend/
βββ src/
β βββ app.module.ts # Root NestJS module β registers all modules
β β
β βββ escrow/ # Escrow module
β β βββ escrow.module.ts
β β βββ escrow.controller.ts # HTTP routes
β β βββ escrow.service.ts # Business logic
β β βββ escrow.repository.ts # DB queries (Prisma)
β β βββ dto/
β β βββ create-escrow.dto.ts
β β βββ update-shipment.dto.ts
β β
β βββ dispute/ # Dispute module
β β βββ dispute.module.ts
β β βββ dispute.controller.ts
β β βββ dispute.service.ts
β β βββ dto/
β β
β βββ auth/ # SEP-10 authentication
β β βββ auth.module.ts
β β βββ auth.controller.ts # /auth/challenge + /auth/verify
β β βββ auth.service.ts
β β βββ guards/
β β βββ jwt.guard.ts
β β βββ admin.guard.ts
β β
β βββ stellar/ # Stellar SDK layer
β β βββ stellar.module.ts
β β βββ blockchain-listener.service.ts # SSE stream from Horizon
β β βββ contract.service.ts # Soroban contract interactions
β β βββ horizon.service.ts # Horizon API utilities
β β
β βββ notifications/ # Email + SMS
β β βββ notifications.module.ts
β β βββ notifications.service.ts
β β βββ templates/ # Email/SMS message templates
β β
β βββ logistics/ # Shipping carrier integrations
β β βββ logistics.module.ts
β β βββ logistics.service.ts # Carrier-agnostic interface
β β βββ providers/
β β βββ terminal-africa.provider.ts
β β βββ gigl.provider.ts
β β
β βββ workers/ # Background jobs
β β βββ auto-release.worker.ts # Cron: checks 48h delivery window
β β βββ tracking-poll.worker.ts # Cron: polls carrier APIs
β β
β βββ admin/ # Admin-only module
β βββ admin.module.ts
β βββ admin.controller.ts
β βββ admin.service.ts
β
βββ prisma/
β βββ schema.prisma
β βββ seed.ts
β βββ migrations/
β
βββ test/
βββ unit/
βββ integration/
βββ e2e/
Key rules:
- Business logic lives in
*.service.tsβ controllers handle HTTP only (parse input, call service, return response) - Database access belongs in
*.repository.tsβ services should not callprismadirectly - Stellar SDK calls belong in
src/stellar/β nostellar-sdkimports elsewhere - All background jobs go in
src/workers/β nosetIntervalor ad-hoc cron calls in services
git checkout main
git pull upstream main
git checkout -b feat/your-feature-name| Type | Pattern | Example |
|---|---|---|
| Feature | feat/description |
feat/gigl-logistics-provider |
| Bug fix | fix/description |
fix/auto-release-duplicate-sign |
| Tests | test/description |
test/dispute-service-integration |
| Docs | docs/description |
docs/add-swagger-to-dispute-dto |
| Refactor | refactor/description |
refactor/extract-escrow-repository |
| Chore | chore/description |
chore/upgrade-prisma-5.x |
- TypeScript strict mode is enabled β
anytypes will be flagged in review - Run
npm run lintbefore committing β ESLint with NestJS rules must pass clean - Run
npm run formatto apply Prettier β don't submit unformatted code - Prefer
async/awaitover raw.then()chains - All thrown errors should be NestJS
HttpExceptionsubclasses β neverthrow new Error()in a controller or service
// β
Controller: thin β parse, delegate, return
@Post()
async createEscrow(
@Body() dto: CreateEscrowDto,
@CurrentUser() vendor: AuthUser,
): Promise<EscrowResponseDto> {
return this.escrowService.createEscrow(dto, vendor.address);
}
// β
Service: logic lives here
async createEscrow(dto: CreateEscrowDto, vendorAddress: string): Promise<Escrow> {
const existing = await this.escrowRepository.findByVendorAndItem(vendorAddress, dto.itemRef);
if (existing) throw new ConflictException("Duplicate escrow for this item reference");
// ... create and return
}
// β Wrong β business logic leaking into controller
@Post()
async createEscrow(@Body() dto: CreateEscrowDto) {
const existing = await this.prisma.escrow.findFirst({ where: { ... } }); // DB in controller
if (existing) throw new ConflictException(...);
}Every request body must have a DTO decorated with class-validator and @ApiProperty (Swagger):
// dto/create-escrow.dto.ts
import { IsString, IsNumber, IsPositive, MinLength } from "class-validator";
import { ApiProperty } from "@nestjs/swagger";
export class CreateEscrowDto {
@ApiProperty({ example: "Vintage Nike Jacket β Size M", description: "Item name shown to buyer" })
@IsString()
@MinLength(3)
itemName: string;
@ApiProperty({ example: 50, description: "Amount in USDC" })
@IsNumber()
@IsPositive()
amount: number;
@ApiProperty({ example: 604800, description: "Shipping window in seconds (default: 7 days)" })
@IsNumber()
@IsPositive()
shippingWindow: number;
}- All Soroban contract interactions go through
ContractServiceinsrc/stellar/ - Transaction submission must use retry logic β Stellar can temporarily reject due to sequence number contention
- The system signer key (
SYSTEM_SIGNER_SECRET) must never be logged, even partially - Always verify the transaction was included in a ledger after submission β don't trust a successful
submitresponse alone - Parse Soroban contract events by
topic+dataXDR, not by position
- Never commit
.envfiles - Never hardcode keys, secrets, or contract IDs in source files β always use
process.envvia theConfigService - The
.env.examplefile must be kept up to date β if you add a new env var, add it there with a placeholder and a comment
This repo uses Conventional Commits.
<type>(<scope>): <short imperative description>
[optional body]
[optional footer: Closes #123]
Types:
| Type | Use for |
|---|---|
feat |
New endpoint, service, module, or worker |
fix |
Bug fix |
test |
New or updated tests |
docs |
Swagger annotations, README, comments |
refactor |
Code restructuring with no behaviour change |
perf |
Performance improvement |
chore |
Dependencies, CI config, tooling changes |
security |
Vulnerability fix or hardening |
Examples:
git commit -m "feat(logistics): add Terminal Africa webhook handler"
git commit -m "fix(auto-release): prevent duplicate transaction submission on retry"
git commit -m "test(dispute): add integration test for admin resolve endpoint"
git commit -m "docs(escrow): add Swagger @ApiProperty to CreateEscrowDto"
git commit -m "security(auth): add replay protection to SEP-10 challenge verification"# Format
npm run format
# Lint β must be clean
npm run lint
# Type check
npm run type-check
# Tests
npm run test
# Build (catches compilation errors)
npm run build- What changed β Plain description of the change
- Why β Link to the issue
- Tests written β Unit and/or integration tests for new code
- Swagger updated β All new/changed endpoints have
@ApiOperation,@ApiResponse, and DTO@ApiPropertydecorators - No secrets β No API keys, contract IDs, or env values hardcoded
- Migration included β If schema changed, the Prisma migration file is committed
-
Closes #123
## Summary
<!-- What does this PR do? -->
## Motivation
<!-- Why is this change needed? Link to the issue. -->
## Changes
<!-- Bullet-point key changes -->
-
-
## Testing
<!-- What tests were added? How was this manually tested? -->
## Migration
<!-- If database schema changed, describe it here -->
## Notes for Reviewer
<!-- Anything reviewers should focus on? -->
Closes #- Active Wave cycle: 48 hours
- Outside Wave: 5 business days
- 1 approving review required
- Changes to Stellar transaction signing, auto-release logic, or dispute resolution require 2 approvals
# Unit tests
npm run test
# Watch mode (during development)
npm run test:watch
# Integration tests (requires a real DB β uses a separate test DB)
npm run test:integration
# E2E tests (requires full stack)
npm run test:e2e
# Coverage report
npm run test:covUnit tests (test/unit/) β Test a single service or function in isolation. Mock all dependencies (Prisma, Stellar SDK, external APIs) using Jest mocks.
// test/unit/escrow.service.spec.ts
describe("EscrowService", () => {
let service: EscrowService;
let mockRepo: jest.Mocked<EscrowRepository>;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
EscrowService,
{ provide: EscrowRepository, useValue: mockEscrowRepository() },
{ provide: ContractService, useValue: mockContractService() },
],
}).compile();
service = module.get(EscrowService);
mockRepo = module.get(EscrowRepository);
});
it("throws ConflictException for duplicate escrow", async () => {
mockRepo.findByVendorAndItem.mockResolvedValue(existingEscrow);
await expect(service.createEscrow(dto, vendorAddress)).rejects.toThrow(ConflictException);
});
});Integration tests (test/integration/) β Test a full module with a real test database (in-memory or Docker). Use @nestjs/testing with a real Prisma client pointing to a test DB.
E2E tests (test/e2e/) β Test full HTTP request-response cycles using supertest. The full NestJS app is bootstrapped against a test DB.
New code must include tests. PRs that drop overall coverage by more than 2% will be asked to add tests before merging.
If your change requires a schema update:
# 1. Edit prisma/schema.prisma
# 2. Generate and apply the migration
npx prisma migrate dev --name describe-your-change
# 3. Regenerate the Prisma client
npx prisma generate
# 4. Commit BOTH the migration files AND schema.prismaMigration rules:
- Never edit existing migration files β create a new one
- Migration names must be descriptive:
add-dispute-resolution-timestamp, notupdate1 - Destructive migrations (dropping columns or tables) must include a comment explaining data handling
- If you're adding a non-nullable column, it must either have a
@defaultvalue or include a migration that backfills existing rows
Do not open a public GitHub issue for security vulnerabilities.
This especially applies to:
- The system signer key handling
- The auto-release transaction submission flow
- SEP-10 authentication and JWT verification
- The admin dispute resolution endpoint
Report privately to:
π§ security@trustlink.xyz (or the address in SECURITY.md)
Include:
- Description of the vulnerability
- Steps to reproduce or a proof-of-concept
- Your severity assessment
We aim to acknowledge within 48 hours and patch critical issues within 7 days.
- π¬ GitHub Discussions β Ask a question
- π GitHub Issues β Confirmed bugs only β include steps to reproduce and error logs
- π Stellar Developers Discord β discord.gg/stellardev for real-time help
Helpful resources:
- NestJS Documentation
- Prisma Docs
- Stellar Horizon API Reference
- Soroban RPC Reference
- Stellar SEP-10 Specification
- Terminal Africa API Docs
The backend is the quiet engine that makes the promise of trustless commerce actually work. Thank you for helping keep it reliable.