You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Trust-Link Backend is a NestJS application that manages escrow transactions on the Stellar blockchain. Vendors create escrow agreements, buyers fund them via Stellar payments, and the system automatically tracks shipments, releases funds, and handles disputes.
Both workers implement OnModuleInit / OnApplicationShutdown for clean startup and graceful shutdown via clearInterval.
AutoReleaseWorker — every 5 minutes
Query escrows in SHIPPED state where deliveredAt ≤ 48 h ago, no open dispute, no autoReleaseTxHash.
Call markAutoReleaseSubmitting(id) — sets autoReleaseSubmittedAt as an optimistic lock.
Submit on-chain auto-release via ContractService.submitAutoRelease(escrowId).
On success: markAutoReleaseCompleted(id, txHash) → state becomes COMPLETED.
On failure: clearAutoReleaseSubmitting(id) — unlocks for next cycle.
TrackingPollWorker — every 10 minutes
Query all SHIPPED escrows with a trackingId.
Call LogisticsService.getStatus(trackingId) for each.
On DELIVERED status: markDelivered(id) + ContractService.recordDelivery(escrowId).
Errors are caught per-escrow so one failure doesn't block the rest.
Stellar Webhook Flow (issue #76 + #77)
Horizon ──POST /webhooks/stellar──► StellarWebhookController
│
verifySignature()
(HMAC-SHA256 with STELLAR_WEBHOOK_SECRET)
│
processedWebhookEvent.findUnique(operationId)
─── already seen? return {skipped: true} ───►
│ first time
processedWebhookEvent.create(operationId)
(DB-persisted cursor — survives restarts)
│
processEvent(dto)
─── type === 'payment'? ──►
│
escrowRepository.findByBuyer(dto.to)
filter state === FUNDED
escrowRepository.updateState(id, FUNDED)
│
─── error? delete cursor, rethrow ──────────►
Caching Strategy (issue #103)
Store: Redis (ioredis), configured via REDIS_URL.
Scope: GET /escrow/:id only — single-record lookups under burst traffic.
Key format: escrow:{uuid} — one key per escrow ID.
TTL: 60 seconds.
Read path: EscrowRepository.findById checks Redis before hitting PostgreSQL.
Invalidation: Every EscrowRepository write method (markShipped, markCancelled, markDelivered, markAutoReleased, updateState, etc.) calls cache.del(escrow:{id}) immediately after the DB mutation.
Graceful degradation: If REDIS_URL is not set, CacheService is a no-op — reads always hit PostgreSQL, no errors.
Authentication
SEP-10 (Stellar Ecosystem Proposal) is used for wallet-native authentication:
Client calls GET /auth with its Stellar public key.
Server returns a signed challenge transaction.
Client signs with its private key and submits to POST /auth.
Server verifies the signature and issues a JWT (SEP10_JWT_SECRET, HS256).
Protected endpoints use @UseGuards(JwtGuard) + @CurrentUser() to extract the verified Stellar address.
Security Controls
Layer
Mechanism
Transport
HTTPS enforced in production
Headers
SecurityMiddleware: CSP, HSTS, X-Frame-Options
CORS
ALLOWED_ORIGINS allowlist; blocks all in production if unset
Auth
SEP-10 JWT on protected routes
Rate limiting
RateLimitGuard with per-route limits (5–100 req/min)