Copied
Systems Deep Dive · Layer 2 · 18 min read

Build Market — Architectural Topography & Core Systems Design

Full systems specification: event-driven backend, non-custodial milestone escrow, statutory state machines, and mechanical ADR governance.

Executive Summary

Build Market is an event-driven marketplace, project-coordination engine, and statutory compliance platform engineered to address the trust deficit, operational fragmentation, and rigorous regulatory requirements of the construction and real estate sectors in East Africa — specifically tailored for the Kenyan market.

The platform connects five distinct stakeholder classes — property owners & developers (individual homeowners, commercial developers, diaspora investors), registered professionals (architects, civil/structural engineers, quantity surveyors, physical planners, and 30+ trade categories), materials suppliers & hardware stores (building material distributors, equipment hire firms), independent dispute inspectors, and statutory regulatory authorities — under a unified, audit-logged ecosystem that introduces transparency, milestone-gated capital protection, and provenance to a historically offline-first industry.

The Problem, and Where the Design Answers It

Real-world problemConsequenceDesign responseSection
Credentials are claimed, not verifiedUnqualified or fraudulent practitioners win workStatutory verification state machine across 9 boards, with human review fallback3.2, 8
Clients pay large deposits upfrontNo recourse if work stalls or is substandardMilestone-gated, non-custodial bank escrow with transparent payout waterfall3.1
Government registers are slow, down, or change schemaOnboarding blocks or failsCache, circuit breaker, async retry stream, operator queue8
Platform could be mistaken for the contractorRegulatory and liability exposureStrict intermediary posture; verification badges are dated factual statementsPhilosophy 1
Heavy handling of national IDs, KRA PINs, and phone numbersPrivacy and breach exposureClass A–D data classification, zero-PII logging, 72-hour breach workflow2.5, 5
One engineer must keep a large system coherentBoundary erosion and driftADRs backed by AST checks and a drift scanner in CI4, 6

System At Scale

DimensionScope & Metrics
Workspace TopologyTurborepo monorepo with 4 isolated applications (apps/client, apps/admin, apps/verification-ops, apps/workers) and 8 shared packages (db, verification-domain, nats, resilience, redis, types, ui, enums)
Data Model4,617-line Prisma schema with 114 relational models, 120 strict enums, PostgreSQL with pgcrypto, citext, full-text search, and query-level OpenTelemetry tracing
Domain Slices24 independent vertical domain slices (properties, projects, verifications, messaging, pipeline, stores, licenses, gdpr, escrow, …) enforcing unidirectional dependency flow
Event Streams6 persistent NATS JetStream streams (VERIFICATION, USERS, ORDERS, PROJECTS, NOTIFICATIONS, LICENSES) with at-least-once explicit acknowledgment and deterministic deduplication
Architectural Governance26 ratified Architecture Decision Records (10 in apps/client [ADR-001–010], 16 in apps/admin [ADR-ADMIN-001–016]) governing layer boundaries, session freshness, and worker isolation
Quality GatesCI-enforced security drift scanner (0 findings across 309 files), environment-contract checks (75 keys), feature-flag governance, and AST boundary tests
Statutory Authorities9 Kenyan regulatory boards validated at the schema and domain level (NCA, EBK, BORAQS, EARB, EPRA, VRB, ISK, NEMA, KEBS)
Geographic CoverageAll 47 Kenyan counties modeled as strict database enums with county-indexed spatial and professional querying
Financial & Tax RailsSafaricom M-Pesa Daraja 2.0 (STK Push C2B, PayBill, B2C automated disbursements, B2B settlements), KRA eTIMS invoice reconciliation, Digital Marketplace Withholding Tax (WHT) waterfalls, and non-custodial bank escrow trust deeds
Deployment TopologyHybrid cloud architecture: Cloudflare Workers edge compute (OpenNext BFF), Render-hosted containerized background daemons (Node.js 24 on Alpine), Upstash Redis (REST rate limiting), and managed PostgreSQL

Core Engineering Philosophy

Build Market is not a CRUD application with marketplace features bolted on. It is a compliance-first, resilient distributed platform where every architectural decision is evaluated against four strict forcing functions:

  1. Statutory Intermediary Reality — Under Kenyan law (Engineers Act 2011, Cap 525 BORAQS Act, NCA Act 2011), the platform must operate strictly as a technology marketplace intermediary. It never warrants building works directly; construction contracts remain strictly bilateral (Homeowner–Contractor), inspection panels are independent, and verification badges are legally factual statements of register confirmation on specific dates.
  2. Legacy Infrastructure Degradation — Government statutory portals (NCA, EBK, BORAQS, KRA) experience frequent downtime, rate-limiting, and unannounced schema drift. The architecture treats external dependency failure as normal operating condition, chaining edge caches, sliding-window circuit breakers, and asynchronous NATS retry streams.
  3. Financial Correctness & Anti-Commingling — In anticipation of the National Payment System Bill (2026) and POCAMLA (2023 AML/CFT Amendment), the platform enforces non-custodial escrow via licensed commercial bank trust accounts. Client funds never commingle with operational capital, all payouts execute via idempotent state machines, and disbursements account for eTIMS validation and statutory withholding tax.
  4. Data Sovereignty & Provable Erasure — Kenya’s Data Protection Act (2019) and ODPC 2021 Regulations mandate strict field-level data classification (Class A–D), 72-hour breach notification playbooks, versioned consent records, and cryptographically verified, tamper-evident GDPR/DPA export and anonymization pipelines.

1. Technical Architecture & Stack Justification

1.1 System Topography

Observability & Telemetry

Persistence & Cloud Storage

Domain Core & Resilience

Asynchronous Messaging & Queues

Identity & Perimeter Defense

Container Runtime (Render)

Web Layer (Next.js Apps · Client on Cloudflare Workers via OpenNext)

traceparent headers

Marketplace Web App
Next.js App Router · OpenNext Edge

Mission-Control Admin
Next.js · Server Actions · RBAC

Verification Operations
Next.js · Operator Queue

Background Worker Daemon
Node.js 24 Alpine · HTTP Liveness Probe

Clerk Identity Provider
Tiered Step-Up Freshness

Dynamic CSP Nonce
Edge Route Guards

AdminCapability Policy
DB AdminProfile Resolution

Upstash Redis (REST)
Edge Sliding Rate Limiter

NATS JetStream
6 Streams · At-Least-Once Delivery

BullMQ (Redis TCP)
High-Concurrency Task Queues

24 Domain Slices
Result<T, DomainError> · Strict Mappers

@build/verification-domain
Statutory Board State Machines

@build/resilience
Circuit Breaker · Jittered Retry · Fallback

Prisma ORM
Tracing · FTS · citext · 114 Models

PostgreSQL Database
Managed Primary · UUID PKs

Cloudflare R2 Bucket
Private Signed URL Direct Uploads

OpenTelemetry Tracer
Distributed Trace Headers

Structured NDJSON Logger
Zero-PII Logging

Edge-to-TCP Connectivity: Because Cloudflare Workers execute in V8 isolates without persistent raw TCP pools, apps/client connects to PostgreSQL via Cloudflare Hyperdrive and Prisma Driver Adapters, enforces sliding-window rate limits over Upstash Redis HTTP REST, and dispatches NATS JetStream events and BullMQ jobs via authenticated internal BFF webhooks to the containerized apps/workers daemon on Render.

1.2 Monorepo Structure

Build Market is structured as a Turborepo monorepo using pnpm workspaces to enforce absolute isolation between presentation adapters, core domain logic, background workers, and persistent schemas:

build-market/
├── apps/
│   ├── client/                  # Marketplace Portal (Next.js App Router, Edge BFF)
│   │   ├── app/
│   │   │   ├── api/             # Thin HTTP route adapters (auth, rate limits, safeParse)
│   │   │   ├── actions/         # Server action adapters (BFF pattern)
│   │   │   └── lib/
│   │   │       ├── domains/     # 24 client-side vertical domain slices
│   │   │       ├── security/    # Role normalization, CSP nonce, route guards
│   │   │       └── validation/  # Zod schema boundaries
│   │   └── lib/facades/         # Browser-safe HTTP client facades
│   ├── admin/                   # Enterprise Mission-Control (Next.js, Control Plane)
│   │   └── src/
│   │       ├── actions/admin/   # safeAction-wrapped mutations with declarative audit
│   │       ├── lib/domains/     # Admin domain services (users, finance, audit, GDPR)
│   │       └── lib/security/    # AdminCapability policy maps & freshness guards
│   ├── verification-ops/        # Statutory Regulator Queue Console (Next.js, Operator Queue)
│   │   └── src/
│   │       ├── app/(dashboard)/ # Specialized operator verification workflows
│   │       └── lib/actions/     # Multi-approver statutory decision actions
│   └── workers/                 # Background Daemon (Node.js 24 Docker Daemon, Worker Container)
│       └── src/
│           ├── consumers/       # NATS JetStream event subscribers
│           ├── queues/          # BullMQ queue workers (Sharp blurhash, PDF BoQ, cleanup)
│           └── healthz.ts       # HTTP liveness & readiness probe
├── packages/
│   ├── db/                      # 4,617-line Prisma schema, extensions, migration history
│   ├── verification-domain/     # Shared statutory board contracts, reason codes & DTOs
│   ├── nats/                    # NATS JetStream client, producer, consumer, metrics
│   ├── resilience/              # Circuit breaker, retry with jitter, timeout, cache
│   ├── redis/                   # Dual-transport: Upstash REST + BullMQ TCP connections
│   ├── enums/                   # Centralized authoritative enums (statutory, financial, counties)
│   ├── types/                   # Cross-cutting TypeScript interfaces & Zod DTO contracts
│   └── ui/                      # Shared design system components (Radix primitives)
└── turbo.json                  # Pipeline build definitions with remote caching

1.3 Stack Justification

LayerTechnologyArchitectural Rationale
Frontend & BFFNext.js App Router + OpenNextServer Actions establish a strict Backend-For-Frontend (BFF) boundary. Sensitive credentials (M-Pesa Consumer Secrets, Clerk Secret Keys) and private business rules never cross to the browser. Edge middleware handles dynamic CSP nonce injection and path-based authorization.
Worker HostNode.js 24 Alpine Daemon on RenderCPU-intensive media transformations (Sharp WebP encoding, Blurhash generation), long-running PDF Bill of Quantities (BoQ) compiling, and continuous NATS message consumption run on isolated worker instances with a dedicated /healthz probe, shielding Next.js web instances from thread starvation (ADR-010 / ADR-ADMIN-016).
DatabasePrisma ORM + PostgreSQLStrict relational modeling for 114 domain entities. pgcrypto handles field-level encrypted secrets; citext ensures case-insensitive email/identity searches; composite GiST/GIN indexes enable full-text search across listings and contractor profiles. Prisma preview tracing enables query-level distributed telemetry.
MessagingNATS JetStreamLow-latency messaging with durable broker persistence across 6 persistent streams. At-least-once explicit acknowledgments decouple high-throughput user interactions from statutory verification checks, SMS dispatch, and invoice reconciliation.
Background QueuesBullMQ over Redis TCPStateful, high-concurrency job scheduling with delayed execution, exponential backoff, and dead-letter handling. Separated from NATS to handle stateful task execution rather than distributed event publishing.
Edge Cache & LimitsUpstash Redis (REST)Serverless HTTP-based rate limiting compatible with Cloudflare Workers edge runtime. Actor-scoped rate limits throttle malicious brute-force attempts and protect fragile external regulator endpoints.
Identity & AccessClerk + DB AdminProfileClerk acts as the runtime identity provider with session freshness tracking. Authorization is capability-based: runtime claims map to an authoritative database AdminProfile, preventing stale token privilege escalation.
Asset StorageCloudflare R2Direct-to-cloud signed upload tokens bypass web application servers. Files are uploaded directly to private S3-compatible R2 buckets with pre-signed authorization, followed by background asynchronous malware scanning and CDN activation.

1.4 Alternatives Considered

DecisionAlternativeWhy this choiceTradeoff accepted
NATS JetStream for events and BullMQ for jobsOne system for bothEvents are published and fanned out; jobs are stateful, delayed, and retried. Each tool fits one shapeTwo systems to operate and monitor
Dedicated worker daemon (ADR-010)Cron or long tasks inside Next.jsImage encoding and queue polling must not starve web threadsAn extra deployable and a /healthz contract
Capability auth resolved from the database (ADR-ADMIN-001)Roles in Clerk JWT metadataJWT claims can be stale or revokedA database read on every admin mutation
Non-custodial escrow via licensed bank or PSPHolding client funds directlyAvoids commingling and payment-system licensing exposureDependence on a bank or PSP partner
Vertical domain slices in one monorepo (ADR-003)Separate services per domainOne engineer, one deploy, with boundaries still enforcedDiscipline is enforced by static checks, not network walls
Strangler-fig with feature flags (ADR-ADMIN-009)Big-bang admin rewriteIncremental production verification and instant rollbackTemporary duplicate routes and flag lifecycle management

2. Architectural Deep-Dives

2.1 Thin Route Adapters & Unidirectional Data Flow

Build Market mandates a strict separation between transport mechanics and business rules. HTTP API route handlers and Server Actions are strictly thin adapters. They contain zero domain logic, zero raw Prisma queries, and zero direct external service calls.

Canonical Data Flow

apiFetch(url, payload)

HTTP + ApiResponse

Actor Context + Validated Input

Prisma Internal Model

Strict Public DTO

Result

Repository Query

Browser / Hook

lib/facades/domain/
*-client.ts

app/api/domain/**
Thin Route Adapter

app/lib/domains/
domain/service.ts

domain/mappers.ts
Strict Boundary

domain/repository.ts
Persistence Only

PostgreSQL
via @build/db

Responsibility Matrix Across Layers

LayerMust OwnMust Never Own
Route / Action AdapterTiming capture (startedAt), correlation ID extraction, actor rate-limiting, Zod .safeParse() (never .parse()), If-Match optimistic concurrency, idempotency lifecycle, resilient executor invocation, HTTP status mapping, structured loggingBusiness invariants, role permission logic, direct Prisma calls, internal entity reshaping
Domain ServiceActor capability validation, business rule execution, cross-repository transactions, Result<T, DomainError> envelope construction, invocation of strict mappersHTTP request/response semantics, status codes, cookies, raw Prisma queries
Mapper (mappers.ts)Deterministic type conversion: Decimal → number, Date → ISO 8601 string, null coalescing, stripping internal/sensitive Class A/B fieldsDatabase calls, external HTTP calls, side effects
RepositoryPure persistence: Prisma reads/writes, soft-delete filtering (deletedAt: null), table lock orders, transactional queriesAuthorization policies, response formatting, user error messaging

Production Implementation: Property Creation Route

The properties POST handler exhibits the canonical 7-step adapter sequence:

// apps/client/app/api/properties/route.ts
export const POST = withAuth(async (req, { dbUserId, userRole }) => {
  // ① Absolute first statement: Start timer & bind correlation context
  const startedAt = now();
  const correlationId = initializeCorrelationId(req);

  // ② Actor-scoped rate limit (protecting against automated scripting)
  const rateLimitResult = await checkRateLimit(
    getActorRateLimitIdentifier(dbUserId, "property-write"),
    RateLimits.WRITE.limit,
    RateLimits.WRITE.window,
  );
  if (!rateLimitResult.success) {
    logger.warn({
      correlationId,
      operationName: "create_property",
      outcome: "rate_limited",
    });
    return apiError("Rate limit exceeded", 429, correlationId);
  }

  // ③ Strict Zod validation (safeParse prevents unhandled runtime exceptions)
  const body = await req.json().catch(() => null);
  const validation = CreatePropertySchema.safeParse(body);
  if (!validation.success) {
    logger.info({
      correlationId,
      operationName: "create_property",
      outcome: "validation_error",
    });
    return apiValidationError(validation.error, correlationId);
  }

  // ④ Idempotency enforcement — Summary key derived from actor and payload
  const idempotencyKey =
    req.headers.get("Idempotency-Key") ||
    IdempotencyService.generateKey(dbUserId, "POST", {
      domain: "property",
      titleHash: hashString(validation.data.title),
    });

  // ⑤ Resilient Domain Execution
  const executionResult = await getResilientExecutor().execute(
    () =>
      propertiesService.createProperty(
        { userId: dbUserId, role: userRole },
        validation.data,
      ),
    { operationName: "create_property" },
  );

  // ⑥ Two-level unwrap: Transport/Infra level followed by Domain Result
  if (!executionResult.success || !executionResult.data) {
    await safeIdempotencyFail(idempotencyKey);
    return apiError("Internal server error", 500, correlationId);
  }

  const domainResult = executionResult.data;
  if (!domainResult.ok) {
    await safeIdempotencyFail(idempotencyKey);
    return apiDomainError(domainResult.error, correlationId);
  }

  // ⑦ Complete idempotency and return standardized API envelope
  await safeIdempotencyComplete(idempotencyKey, domainResult.data);
  return apiSuccess(domainResult.data, 201, correlationId);
});

2.2 Capability-Based Admin Security Model

The administrative control center (apps/admin) abandons coarse, vulnerable role-string comparisons (if (role === "ADMIN")) in favor of an exhaustive, capability-based security model backed by ADR-ADMIN-001 and ADR-ADMIN-008.

Admin Actor Resolution & Trust Boundary

// Verified Admin Actor Shape (ADR-ADMIN-001)
export type AdminActor = {
  clerkId: string; // Verified identity claim from active Clerk session
  dbUserId: string; // Primary key in PostgreSQL database
  adminRole: AdminRole; // Resolved strictly from DB AdminProfile (never Clerk metadata)
  sessionId: string; // Active session identifier for freshness calculation
  sessionAuthTime: Date; // Timestamp of last re-authentication
};

The safeAction Security Pipeline

Every administrative mutation is wrapped in safeAction, executing a non-bypassable 7-step pipeline:

1. Resolve Clerk Identity Claim

2. Fetch & Validate Active AdminProfile

3. Validate Account State (Active vs Suspended)

4. Enforce AdminCapability Policy Matrix

5. Enforce Step-Up Session Freshness
(Tier 1 / Tier 2 time-bounded freshness windows)

6. Execute Domain Service via Safe Parse

7. Append-Only Declarative Audit Log Write

Step-Up Session Freshness Tiers

Freshness TierWindowOperations GovernedFailure Behavior
Tier 1 — CriticalStrict short-lived re-auth windowRole modifications, user suspension/deletion, manual financial reversals, escrow release, PII data exportsThrows STEP_UP_AUTH_REQUIRED modal prompting password/biometric re-verification
Tier 2 — ElevatedElevated re-auth windowStatutory license overrides, contractor category verification, dispute referee assignmentThrows STEP_UP_AUTH_REQUIRED
StandardSession DefaultRead-only analytics, support ticket triage, content moderationStandard session expiration

Granular Capability Enforcement

// ✅ Mandatory: Verified capability-based evaluation
await requireAdminCapability(actor, AdminCapability.OVERRIDE_VERIFICATION);

// ❌ Tracked critical defect: Prohibited raw role-string evaluation
if (actor.adminRole === "SUPER_ADMIN" || actor.adminRole === "ADMIN") { ... }

2.3 Event-Driven Orchestration with NATS JetStream

Cross-service communication and background processes are orchestrated through NATS JetStream, completely decoupling synchronous user latency from heavyweight downstream operations.

Stream Topology & Retention Guarantees

Build Market configures 6 persistent streams with strict, domain-tailored retention and deduplication profiles:

Stream NameSubjectsRetention PolicyMax AgeDeduplication WindowPurpose
VERIFICATIONverification.>Limits7 daysNo msgId (server default window)Contractor & property credential lifecycle tracking
USERSuser.>Limits30 daysNo msgId (server default window)Account lifecycle, consent records, profile updates
ORDERSorder.>Limits90 daysNo msgId (server default window)Materials purchase orders, BoQ checkouts, store items
PROJECTSproject.>Limits90 daysNo msgId (server default window)Construction milestones, site sheets, change orders
NOTIFICATIONSnotification.>WorkQueue24 hoursNo msgId (server default window)Transactional SMS (Africa’s Talking), push, email
LICENSESlicense.>Limits30 days120 secondsStatutory regulatory board checks & manual overrides

Explicit Acknowledgment Lifecycle

Message consumers adhere to an explicit acknowledgment contract. Handlers never interact with raw broker primitives; the consumer framework guarantees that failures result in either exponential backoff retries or dead-letter termination:

Message Arrives at Consumer
      │
      ▼
Decode JSON Payload ────► [Decode Corrupted?] ────► msg.term()  (Poison-pill termination)
      │
      ▼
Restore OpenTelemetry Parent Span Context
      │
      ▼
Invoke Registered Domain Handler
      │
      ├─ Throws Transient Error ──► Delivery Count Exhausted?
      │                                  ├─ Yes ──► Publish to Dead-Letter Subject & msg.term()
      │                                  └─ No  ──► msg.nak(exponentialBackoffDelay)
      │
      └─ Resolves Successfully ───► msg.ack()  (Removed from redelivery queue)

The Invariant: msg.ack() is invoked only after the database transaction commits. In the event of an ungraceful node crash between the database write and acknowledgment, the message is redelivered. Because all domain event handlers are designed with idempotent upsert semantics, redelivery produces zero inconsistent state.

Deterministic Content-Addressed Deduplication

To prevent duplicate processing during network partitions or retry storms, messages published to sensitive streams use deterministic, content-addressed IDs:

// Deterministic message ID generation for deduplication
const msgId = `${licenseId}-verify-${verificationAttemptNumber}`;
await producer.publishWithRetry("license.auto_verified", eventPayload, {
  msgId,
  maxRetries: 3,
});

Within the LICENSES stream’s 120-second deduplication window, any duplicate message with the same msgId is silently dropped by the NATS server, eliminating duplicate verifications.


2.4 Production Resilience Pipeline

Domain calls wrap inside the custom @build/resilience pipeline. Rather than arbitrary try/catch blocks, resilience behaves as a declarative policy engine combining timeouts, circuit breakers, jittered exponential backoffs, and fallback caches:

Cache Hit

Cache Miss

Closed

Open

Success

Failure

Domain Operation

Cache Check
TTL + SWR

Timeout Guard
Criticality Based

Circuit Breaker
Sliding Window

Retry Engine
Exponential + Jitter

Execute Call

Fallback Strategy

OperationResult

Criticality Tier Profiles

Criticality TierTarget OperationsTimeoutRetry StrategyCircuit Breaker PolicyCache Strategy
CriticalEscrow releases, M-Pesa STK push, user bans3,000ms0 retries (fail fast)Threshold: 3 failures / 30sNone
NormalProperty search, contractor listings, store items5,000ms3 attempts, exponential backoff (200ms base) with jitterThreshold: 5 failures / 60s60s TTL, 30s Stale-While-Revalidate
NormalProperty search, contractor listings, store items5,000ms3 attempts, exponential backoff with jitterThreshold: 5 failures / 60s60s TTL, 30s Stale-While-Revalidate
Statutory PortalNCA, EBK, BORAQS, KRA registry checks8,000ms3 attempts + 24h NATS retry streamThreshold: 3 consecutive failures / 60s48h Verified Cache TTL
BackgroundPDF BoQ compilation, analytics, catalog sync15,000ms5 attempts with exponential backoffThreshold: 10 failures / 120s300s TTL

2.5 GDPR & Kenya Data Protection Act Compliance

Data privacy is architected directly into the database schema and storage policies, meeting both the Kenya Data Protection Act (2019) / ODPC 2021 Regulations and GDPR/UK GDPR (critical for diaspora property investors).

Data Classification Matrix

Data ClassConcrete ExamplesStructured LogsBrowser StorageAudit Trail Persistence
Class A: RestrictedM-Pesa API secrets, Clerk signing keys, database credentialsProhibitedProhibitedNever stored
Class B: Sensitive PIINational ID (ID/Passport), KRA PINs, phone numbers, email addresses, site GPS coordinates, KYC identity scansStrictly Masked / ProhibitedProhibitedExplicit opt-in only with automated retention limits
Class C: Internal OperationalUUID resource IDs, correlation IDs, timestamps, capability flags, state machine enum statusesAllowedAllowedAppend-only audit records
Class D: Public DomainVerified business names, public portfolio photographs, county listing regions, store opening hoursAllowedAllowedPublic read-replicas

Compliance Data Architecture

// Core Compliance Records in packages/db/prisma/schema.prisma
model ConsentRecord {
  id               String       @id @default(uuid())
  userId           String
  consentType      ConsentType  // TERMS_OF_SERVICE, PRIVACY_POLICY, MARKETING_COMMUNICATIONS
  version          String       // E.g., "v2.1"
  ipAddress        String?      // Provenance capture
  userAgent        String?
  grantedAt        DateTime     @default(now())
  withdrawnAt      DateTime?
  user             User         @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([userId, consentType])
  @@index([consentType, grantedAt])
}

model AuditLog {
  id               String       @id @default(uuid())
  actorUserId      String
  actorRole        AdminRole
  action           AuditAction
  targetResource   String
  targetResourceId String
  legalBasis       LegalBasis   // CONSENT, CONTRACT, LEGAL_OBLIGATION, LEGITIMATE_INTEREST
  metadata         Json         // Class C/D metadata diffs only (Zero PII)
  ipAddress        String
  createdAt        DateTime     @default(now())

  @@index([actorUserId, createdAt])
  @@index([targetResource, targetResourceId])
}

72-Hour Incident Response Workflow

Under Section 43 of Kenya’s DPA 2019, data breaches presenting risks to user rights must be reported to the Office of the Data Protection Commissioner (ODPC) within 72 hours of awareness. The system includes automated incident logging (SecurityIncident model) that records:

  1. Affected data classification tiers (Class A through D).
  2. Number of affected records and user identifiers.
  3. Cryptographic hash of impacted data categories.
  4. Exportable compliance dossier containing incident timelines, root cause analysis, and remediation actions for formal submission to the ODPC.

2.6 Direct-to-Cloud Asset Management Pipeline

To prevent Next.js server threads and edge workers from blocking on massive multi-megabyte payloads (architectural blueprints, site inspection videos, store catalogs), Build Market implements a direct-to-cloud signed upload pipeline via Cloudflare R2:

Background Worker DaemonCloudflare R2 BucketNext.js API AdapterClient BrowserBackground Worker DaemonCloudflare R2 BucketNext.js API AdapterClient BrowserPOST /api/uploads/signed-url (filename, mimeType, size)1Validate file type & size quota, generate signed token2Signed Upload URL + Asset Tracking ID3PUT [Binary Payload] directly to Signed URL4200 OK (ETag returned)5POST /api/uploads/confirm (Asset ID, ETag)6Enqueue "asset.activate" task to BullMQ7Validate upload, run malware check, generate WebP & Blurhash8Mark Asset status = ACTIVE9

Orphaned uploads (signed tokens issued but never confirmed) are automatically pruned after 24 hours by an automated BullMQ cleanup worker.


2.7 Background Worker Isolation & Queue Semantics

In compliance with ADR-010 and ADR-ADMIN-016, background jobs are decoupled from web applications and consolidated into a dedicated worker daemon (apps/workers):

  1. Separation of Concerns: Next.js web application instances handle fast HTTP request/response lifecycles. They never process long-running tasks, heavy image encoding, or continuous queue polling.
  2. Dual-Transport Redis Architecture:
    • Upstash Redis (REST): Stateless, edge-compatible HTTP calls used for actor rate-limiting in Cloudflare Workers edge middleware.
    • Redis TCP Connection: High-throughput, persistent TCP connection used by BullMQ within the containerized Render worker daemon.
  3. Liveness & Readiness Health Probes: The worker daemon exposes an HTTP endpoint on an HTTP liveness probe (/healthz). Container orchestrators (Render/Docker) continuously query this probe. If NATS subscriptions stall or the Redis TCP connection disconnects, the probe fails and triggers container recreation.

3. Localized Kenyan Ecosystem Integrations

Build Market embeds the Kenyan financial, legal, and operational landscape directly into the domain logic and schema definitions.

3.1 M-Pesa Financial Integration & Non-Custodial Escrow

The platform integrates Safaricom M-Pesa Daraja 2.0 across three transaction modes with strict idempotency:

ModePurposePrisma ModelIdempotency Anchor
MPESA_STK_PUSHHomeowner milestone escrow fundingMpesaTransactionUnique composite: [merchantRequestId, checkoutRequestId]
MPESA_PAYBILLDirect merchant payments & deposit collectionsMpesaTransactionUnique: [idempotencyKey]
MPESA_B2CProfessional payouts upon milestone releaseMpesaB2CUnique composite: [conversationId, transactionId]

Non-Custodial Escrow Architecture

To comply with the National Payment System Act and the NPS Bill 2026, Build Market does not custody client funds. Milestone payments are held in an escrow trust account managed by a licensed commercial bank or regulated Payment Service Provider (PSP). The platform maintains an immutable internal claims ledger. Funds release occurs strictly upon verified milestone completion or independent inspector sign-off.

Transparent Milestone Payout Waterfall

[Homeowner Milestone Deposit: KES 100,000]
                     │
                     ├─► KRA Withholding Tax (5% Resident WHT): KES 5,000
                     ├─► Build Market Platform Fee (3% + VAT): KES 3,480
                     │       ├─ Net Platform Revenue: KES 3,000
                     │       └─ 16% VAT on Fee: KES 480
                     │
                     └─► Net Disbursement to Contractor via M-Pesa B2C: KES 91,520

Every payee receives a deterministic financial breakdown prior to milestone acceptance, preventing payment disputes and ensuring complete tax auditability. Figures above are illustrative; applicable WHT and VAT treatment varies by payee category.

  • Shortcode & Float Custody: The M-Pesa PayBill and B2C disbursement shortcodes are held under the regulated PSP / commercial bank escrow trust partner; Build Market holds API initiator credentials scoped to signed payout instructions, never custody of the underlying float.
  • High-Value Milestones (> KES 250,000): While M-Pesa STK Push and B2C cap single transactions at KES 250,000, milestones exceeding that threshold route through PesaLink or RTGS bank-to-trust transfers using the same idempotent EscrowClaim ledger state machine.
  • Statutory Withholding Tax (WHT): Under the Kenya Income Tax Act (Cap 470, Third Schedule), resident WHT is dynamically calculated at 3% for registered building/civil contractors and 5% for professional/management consultancy services (EBK, BORAQS), alongside 16% VAT on the platform fee.

Milestone Escrow Lifecycle (Illustrative)

Independent InspectorContractorBank Trust Account / PSPBuild MarketHomeownerIndependent InspectorContractorBank Trust Account / PSPBuild MarketHomeowneralt[Homeowner approves][Dispute raised]Fund milestone (M-Pesa STK Push)1Record claim in internal ledger (idempotent on request IDs)2Funds held in escrow trust account3Submit milestone completion evidence4Approve milestone5Assign independent inspection6Sign-off or rejection7Compute waterfall (WHT, platform fee, VAT)8Release instruction (Tier 1 step-up if manual)9Net payout via M-Pesa B2C (idempotent on conversation ID)10Close ledger entry, reconcile eTIMS invoice11

3.2 Statutory Licensing Authority Registry

Kenya’s built environment requires multi-agency oversight. The database models all 9 statutory licensing bodies as strict enums:

Authority CodeFull Regulatory BodyRegulated Professional CategoriesVerification Pipeline
NCANational Construction AuthorityBuilding, civil, electrical, and mechanical contractors (NCA 1 through NCA 8)Registration number + Contractor Category check
EBKEngineers Board of KenyaConsulting, professional, and graduate engineers (Civil, Structural, Mech, Elec)Valid practicing license cross-check
BORAQSBoard of Registration of Architects and Quantity SurveyorsArchitects, quantity surveyors, landscape architectsRoll number and annual practicing certificate
EARBEstate Agents Registration BoardReal estate agents and property managersAnnual practicing certificate validation
EPRAEnergy and Petroleum Regulatory AuthorityElectrical technicians and solar installation contractorsClass of license (Class A, B, C) verification
VRBValuers Registration BoardProperty valuers and asset appraisersAnnual gazetted register confirmation
ISKInstitution of Surveyors of KenyaLand surveyors, geospatial engineers, valuation surveyorsMembership tier & professional standing
NEMANational Environment Management AuthorityEnvironmental Impact Assessment (EIA) lead expertsEIA/EA license verification
KEBSKenya Bureau of StandardsBuilding material manufacturers and testing labsDiamond Mark of Quality certification verification

The ProfessionalLicense model enforces an @@unique([authority, licenseNumber]) constraint and tracks verification through an audit-backed state machine:

Professional Submits License

Operator Claims Case

Register Match Confirmed

Discrepancy Found (Document Resubmission Requested)

Corrected Evidence Provided

Fraudulent / Unregistered Credential

Annual License Lapse

Disciplinary Board Notice

PENDING

IN_REVIEW

VERIFIED

NEEDS_CORRECTION

REJECTED

EXPIRED

SUSPENDED


3.3 County Geography & eTIMS Tax Compliance

  • 47 County Enums: All 47 Kenyan counties (NAIROBI, KIAMBU, MOMBASA, NAKURU, KISUMU, …, MANDERA) are enforced as native database enums. Spatial queries and contractor discovery leverage composite indexing:
    @@index([profession, county, verificationStatus])
  • KRA eTIMS Integration: Kenya Revenue Authority mandates electronic tax invoice validation for all business-to-business transactions. The order and escrow pipelines capture the vendor’s own eTIMS invoice QR code and cryptographically signed fiscal confirmation, guaranteeing that expenses are tax-deductible for corporate property developers.
  • Currency & Precision: All monetary amounts are stored using PostgreSQL Decimal(12, 2) to eliminate floating-point rounding errors. Operational limits enforce Safaricom Daraja boundaries: minimum disbursement of KES 10 and single-transaction maximum of KES 250,000.

4. Architectural Decision Records (ADRs)

Build Market is governed by 26 ratified Architecture Decision Records across apps/client and apps/admin. These documents define the rules of the monorepo, and Section 6 shows how they are enforced.

On a one-person team there is no second reviewer to catch a boundary violation, so every ADR that can be checked mechanically is backed by an AST test, a drift scanner, or a contract check in CI. The ADRs are the specification; the gates are the reviewer.

Selected Core Monorepo ADRs

ADR ReferenceTitleContext & Decision SummarySystem Consequence
ADR-001Clerk as Identity ProviderSession tokens represent authentication identity only. Authorization is calculated from database profile roles.Synchronization to Clerk metadata is required upon role change; failure rolls back the mutation.
ADR-002Layer Boundaries & Import DirectionPrevent business logic leakage into route handlers.Import direction is strictly Adapters → Services → Repositories. Facades communicate via HTTP only.
ADR-003Domain Slicing & Boundary EnforcementMonolithic models cause circular dependencies.Domain slices are isolated. Cross-slice communication requires public interfaces or NATS events.
ADR-006Data Classification & Replay StoragePrevent PII persistence in idempotency stores.Replay payloads permit only Class C/D data. Class B fields require explicit policy registry opt-in.
ADR-010Worker Daemon IsolationHeavy jobs block Next.js server threads.Background tasks run in dedicated Node.js 24 Alpine daemons on Render with HTTP /healthz monitoring.
ADR-ADMIN-001Admin Capability-Based AuthCoarse role strings create authorization vulnerabilities.AdminCapability policy map controls all actions. Session freshness enforced (tiered windows).
ADR-ADMIN-002Admin Layer StructureEliminate direct Prisma access inside action files.All mutations wrap in safeAction and delegate to domain services returning Result<T, DomainError>.
ADR-ADMIN-008Declarative Audit Log ContractManual logging leads to omitted audit entries.High-risk operations declare auditLog in action options; entries write to DB before success returns.
ADR-ADMIN-009Strangler-Fig & Feature FlagsPrevent big-bang deployment failures.New behavior deploys behind typed AdminFeatureFlag values with automated flag lifecycle limits.
ADR-ADMIN-011Telemetry & Observability SLOsUnmonitored degraded performance impacts users.Standardized APM metrics: P95 latency < 350ms for APIs, error budget monitoring, zero PII logging.
ADR-ADMIN-016Background Worker Daemon MigrationContainerized worker migration from legacy cron.Standalone Docker worker deployment with continuous queue health probes and graceful shutdown.

5. Observability & Operational Readiness

Observability is enforced as a compiler and linting requirement across all application layers.

Structured Log Contract

Every adapter emits NDJSON structured logs using Pino:

export type StructuredLogEvent = {
  correlationId: string; // Request-scoped UUID propagated across services
  operationName: string; // Stable identifier (e.g., "admin_verify_contractor")
  actorRole: AdminRole; // Role enum (never personal identity claims)
  outcome:
    | "success"
    | "domain_error"
    | "validation_error"
    | "rate_limited"
    | "internal_error";
  httpStatus: number;
  durationMs: number; // Elapsed execution time from start to response
  resourceType?: string; // Target domain resource
};

Hard Zero-PII Policy

Log sanitizers automatically filter the following fields:

  • userId, clerkId, nationalId, idNumber, kraPin
  • email, phone, phoneNumber, mpesaNumber
  • Plaintext request bodies, response payloads, and SQL parameter values.

Distributed OpenTelemetry Tracing

Distributed trace headers (traceparent) inject into NATS message headers on publish and are extracted by downstream consumers, providing end-to-end waterfall visualization:

Browser Request ──► Next.js Edge Route (Trace Start)
                         │
                         ├─► Prisma Query: Find Active Property
                         ├─► Redis: Check Rate Limit & Cache
                         └─► NATS JetStream: Publish "project.milestone_approved"
                                   │
                                   ▼ [Trace Context Injected into Headers]
                             NATS JetStream Broker
                                   │
                                   ▼ [Trace Context Restored by Consumer]
                             Worker Daemon Consumer
                                   ├─► BullMQ Task: Compile Milestone BoQ PDF
                                   └─► Safaricom Daraja API: Initiate B2C Payout

6. Testing, Drift Gates & Verification Strategy

Quality gates run across CI/CD pipelines to guarantee architectural integrity and prevent security regressions.

Multi-Tier Testing Pyramid

Test SuiteExecution ScopeVerification Target
Policy Matrix TestsAdminRole × AdminCapability × ActionValidates that unauthorized roles receive forbidden and cannot elevate privileges
Action Boundary TestsAST static analysis across actions/Confirms zero direct Prisma imports and zero .parse() calls in adapter files
Domain Service TestsVitest unit testsValidates business invariants, state machine transitions, and error codes
Contract & Drift TestsCustom validation scriptsAsserts that all environment variables match templates and zero unapproved feature flags exist
Critical Journey E2EPlaywright / CypressValidates complete user journeys: user onboarding, license submission, escrow release

Verification Toolchain

# Typecheck across monorepo
pnpm run check-types

# Automated security drift scanner (must pass with 0 findings across 309 files)
pnpm run admin:report-security-drift:strict

# Environment contract verification (asserts all 75 keys match template schemas)
pnpm run admin:check-env-contract
pnpm run client:check-env-contract

# Feature flag governance (asserts no expired or unowned flags exist)
pnpm run admin:check-governance

# Full test suites
pnpm run client:test:all
pnpm run admin:test:all

7. Admin Overhaul & Strangler-Fig Evolution

The administrative system (apps/admin) underwent a comprehensive, 12-phase strangler-fig migration to remediate architectural debt and elevate security posture.

Migration Progress & Milestones

PhaseStrategic ObjectiveMilestone DeliverablesStatus
Phase 0–2Structural Autopsy & ADR FoundationRatified ADR-ADMIN-001 through 009; established strict env boundary; built automated drift detection tooling✅ Complete
Phase 3Auth & Identity HardeningDeployed AdminActor resolution; implemented safeAction pipeline with tiered freshness gates✅ Complete
Phase 4Domain/Repository Layer ExtractionExtracted 13 domain services returning Result<T, DomainError>; isolated repositories✅ Complete
Phase 5Action Slice MigrationMigrated all actions to safeAction; eliminated raw .parse() calls; centralized validation✅ Complete
Phase 6Client Facade & DTO StandardizationEnforced mappers.ts boundaries stripping Class A/B fields and converting Decimal/Date types✅ Complete
Phase 7–8Observability & Declarative AuditImplemented structured NDJSON logging; wired declarative auditLog into safeAction✅ Complete
Phase 9Step-Up Session Freshness GatesAutomated AdminRole × AdminCapability permutation testing across all mutations✅ Complete
Phase 10Feature Flag InfrastructureBuilt typed AdminFeatureFlag system for canary releases and zero-downtime rollbacks✅ Complete
Phase 11Verification Console ExtractionExtracted regulatory review queue into apps/verification-ops with @build/verification-domain✅ Complete
Phase 12ASVS L2 Security HardeningMass-assignment elimination; CSP nonce headers; drift scanner passing with 0 findings (309 files)✅ Complete
Flag RetirementStrangler-Fig Completion (2026-09-02)Retired 4 v2 feature flags; deleted obsolete /users-v2, /verifications-v2, /audit-v2 routes; promoted canonical routes✅ Complete
Worker MigrationDaemon Isolation (ADR-ADMIN-016)Decoupled background queue processors into standalone Render Docker daemon with HTTP liveness probe✅ Complete

8. Designing for Legacy Unreliability

The Challenge

Kenyan government portals (NCA, EBK, BORAQS, KRA) experience routine downtime, unannounced schema modifications, and intermittent network timeouts. A synchronous dependency would degrade platform availability and abandon user onboarding flows.

The Multi-Stage Degradation Pattern

Cache Hit (Valid)

Cache Miss

Circuit Closed (Healthy)

HTTP 200 OK

Timeout / 5xx Error

Circuit Open (Degraded)

Retries Exhausted

Operator Confirms Register

Professional Submits License

Check Redis / DB Verification Cache
(TTL: 48 Hours)

Circuit Breaker Evaluation

External Regulator Portal
(NCA / EBK / BORAQS)

Publish to NATS LICENSES Stream
(Exponential Jittered Retry)

Route to apps/verification-ops
(Operator Manual Review Queue)

Status: PENDING_VERIFICATION
(User Journey Remains Unblocked)

Status: VERIFIED

Trip Circuit Breaker

  1. Edge Cache Verification: Previous validations cache for 48 hours to eliminate redundant external portal requests.

  2. Circuit Breaker Fast-Fallback: If 3 consecutive requests to a statutory portal time out, the circuit opens. Subsequent requests bypass the external portal instantly without blocking user requests.

  3. Asynchronous Retry Stream: Pending verifications publish to the LICENSES NATS JetStream stream, retrying with exponential backoff across a 24-hour window.

  4. Operator Verification Console (apps/verification-ops): If automated verification fails after all retries, the case surfaces on the operator queue with duplicate license warnings and two-approver controls.


9. Failure Modes & Threat Model

Reliability and security claims are only useful if each maps to a concrete behavior. The tables below consolidate them.

Failure Modes

FailureDetectionSystem behaviorSection
Process crashes after DB commit, before ack()Broker redelivery after ack-waitMessage redelivered; idempotent upserts produce no duplicate state2.3
Duplicate publish during a retry stormJetStream msgId deduplication (LICENSES, 120s)Duplicate dropped server-side2.3
Corrupt or schema-invalid payloadDecode or schema failuremsg.term(); no infinite redelivery loop2.3
Transient handler errorThrown errornak() with exponential backoff; dead-letter subject once delivery count is exhausted2.3
Slow government portal callLong-running handlermsg.working() heartbeat prevents false redelivery11
Regulator portal downCircuit breaker opensStatus PENDING_VERIFICATION; async retry stream; operator queue after retries8
Worker loses NATS subscription or Redis TCP/healthz failsContainer recreated by the orchestrator2.7
Signed upload never confirmedUnconfirmed asset recordPruned after 24 hours by a BullMQ cleanup worker2.6
Duplicate or retried M-Pesa requestUnique composite keysIdempotent write; no double disbursement3.1
Stale session attempts a critical admin actionFreshness checkSTEP_UP_AUTH_REQUIRED (tiered freshness)2.2
Concurrent milestone transactionsDeterministic lock orderingNo circular lock waits11

Threat Model (Threat → Control)

ThreatControlSection
Stale or forged token escalates privilegeadminRole resolved from database AdminProfile on every mutation; capability policy matrix2.2
Session hijack during a sensitive operationStep-up freshness tiers2.2
Mass assignment and unvalidated inputZod .safeParse() at every adapter; Phase 12 mass-assignment elimination (ASVS L2 target)2.1, 7
PII leaking into logs or replay storageZero-PII log sanitizers; ADR-006 allows only Class C/D in idempotency replay2.5, 4, 5
Scripted abuse and regulator hammeringActor-scoped rate limits at the edge1.3
Malicious file uploadPrivate bucket, signed direct upload, background malware check2.6
Replayed or duplicated payoutsIdempotency anchors and deterministic message IDs3.1, 2.3
Missing audit trailDeclarative auditLog written to DB before success returns2.2, 4
Architectural drift weakening controlsAST boundary tests and drift scanner block merges6
Script injectionDynamic CSP nonce at the edge1.1

10. Engineering Tradeoffs & Retrospective

What Worked

  • Domain Extraction: Decoupling route adapters from business logic made unit testing straightforward. Policy matrix tests now run hundreds of permutations in seconds without database mocking.
  • Declarative Audit Logging: Attaching audit metadata directly to safeAction options eliminated manual audit omission bugs.
  • Dedicated Worker Daemons: Migrating background workers to a standalone Render Docker container eliminated memory spikes on web nodes and prevented image processing from starving customer traffic.
  • Strangler-Fig Route Retirement: Feature-flagged v2 routes allowed incremental production verification. Retiring flags and deleting legacy code paths preserved codebase health.

What Was Refactored (Resolved Defects)

Earlier architectural iterations carried technical debt that has since been resolved:

  • ADM-011 (Resolved): Eliminated safeVerificationAction by unifying all admin mutations under the canonical safeAction pipeline.
  • ADM-014 (Resolved): Decomposed a 952-line monolithic shared.ts helper file into modular _core action infrastructure (context.ts, validation.ts, audit.ts).
  • ADM-016 (Resolved): Standardized cross-application domain result types on the canonical ok: boolean discriminant, eliminating confusion between ok and success.
  • Defect Baseline: As documented in apps/admin/docs/DEFECTS.md, there are no open high- or medium-severity architectural defects.

Active Engineering Focus

  • NATS Dead-Letter Queue (DLQ) Subject Alerting: Poison-pill messages terminate cleanly via msg.term(), but forwarding dead-letter events directly to PagerDuty/Slack alerting remains an active enhancement.
  • KRA Automated Direct Clearing: Preparing the eTIMS validation engine to support direct automated clearance APIs as KRA rolls out machine-to-machine endpoints.

What I’d Do Differently

  • Enforce boundaries mechanically from day one. The 952-line shared.ts (ADM-014) and the v2 strangler migration were the cost of adding layer rules after the code existed.
  • Fix result-type conventions before the second slice. Reconciling ok and success discriminants (ADM-016) was avoidable churn.
  • Ship progressive onboarding before full KYC gates. Early contractor feedback showed that asking for all statutory certificates on screen one caused drop-off; decoupling profile creation from asynchronous verification (PENDING_VERIFICATION) let professionals build their catalog immediately while verification ran in the background.
  • Write the ADR before the first line of a new slice. The ADRs that mattered most were the ones that predated the code they governed.

11. Key Technical Differentiators

Architectural PillarImplementation DecisionOperational & Business Value
Delivery GuaranteesAt-least-once via explicit ack() after DB commitPrevents dropped operations during ungraceful process crashes
Financial IntegrityNon-custodial escrow + deterministic M-Pesa idempotency keysEliminates duplicate disbursements and ensures compliance with NPS Bill 2026
Deadlock PreventionStrict, deterministic table lock ordering in multi-model transactionsPrevents circular lock waits under concurrent milestone transactions
Slow External OperationsHeartbeat msg.working() signals during external portal callsPrevents false broker redeliveries during slow government queries
Poison-Pill Mitigationmsg.term() invocation upon schema invalidation or exhausted retriesPrevents infinite crash-loop cycles on corrupted message payloads
Session ElevationTiered session freshness enforcement (strict short-lived re-authentication windows)Neutralizes session hijacking risks during sensitive administrative operations
Data Protection4-tier data classification (Class A–D) + 72-hour ODPC breach workflowsEnforces provable Kenya DPA (2019) and GDPR compliance
Distributed TelemetryOpenTelemetry trace context propagated across NATS message headersProvides complete, unified waterfall visibility from browser to background worker
Worker IsolationContainerized Node.js 24 Alpine daemon on Render with /healthz probeIsolates heavy CPU workloads from user-facing Next.js web application servers
Quality Drift GatesStrict static analysis checking environment boundaries and security driftBlocks merges that violate layer boundaries or introduce direct process.env reads

12. Outcomes, Evidence, and Screenshots

The sections above prove how the system is built. This section should prove that it works. Fill in what you can measure; omit what you cannot.

Evidence to Add

  • Adoption: registered users, professionals, verified licenses, stores listed, transactions or KES escrowed (even pilot numbers).
  • Verification performance: share of licenses verified automatically versus routed to operators, and median time to verified.
  • Measured latency: actual P95 API latency against the 350ms SLO in ADR-ADMIN-011.
  • Reliability: worker uptime, dead-letter volume, and any incident you handled with the 72-hour workflow.
  • Delivery: CI duration with Turborepo remote caching, test counts per suite.
  • Cost: monthly infrastructure cost. Running this stack solo on a small budget is itself a result.

Screenshot Shot List

  1. Marketplace listing with county and profession filters.
  2. Contractor profile showing a dated, authority-attributed verification badge.
  3. Milestone funding with the M-Pesa STK Push prompt.
  4. Payout waterfall shown to the contractor before acceptance.
  5. Admin step-up re-authentication modal.
  6. Verification-ops queue with a duplicate-license warning and two-approver control.
  7. A distributed trace waterfall from browser through NATS to the worker.

Appendix A: Glossary

TermMeaning
KRAKenya Revenue Authority
eTIMSKRA’s electronic Tax Invoice Management System for validated invoices
WHTWithholding tax deducted at source from a payment
STK PushM-Pesa prompt on a customer’s phone to authorize a payment
C2B / B2C / B2BCustomer-to-business, business-to-customer, and business-to-business M-Pesa transaction types
PSPPayment Service Provider
ODPCOffice of the Data Protection Commissioner
DPA 2019Kenya Data Protection Act, 2019
POCAMLAProceeds of Crime and Anti-Money Laundering Act
BoQBill of Quantities: an itemized list of materials, labor, and costs for a build
ADRArchitecture Decision Record
DLQDead-letter queue: where messages go after retries are exhausted

Build Market demonstrates that engineering for emerging markets demands heightened architectural rigor. Unreliable external infrastructure, rigorous statutory regulations, and mobile-first financial rails cannot be treated as edge cases — they must be embraced as core system constraints. Every boundary, circuit breaker, and state machine in this platform exists because a real-world operational failure mode demanded it.