Skip to content

Technical Stack ​

The technology powering Yebo.


Overview ​

┌──────────────────────────────────────────────────────────────┐
│                       PRESENTATION                            │
│  Yebo Chat (Web)  •  Mobile App (Flutter)  •  SMS Fallback   │
└──────────────────────────────────────────────────────────────┘
                              │
┌──────────────────────────────────────────────────────────────┐
│                         GATEWAY                               │
│  Cloudflare (CDN, WAF, Workers)  •  Load Balancer            │
└──────────────────────────────────────────────────────────────┘
                              │
┌──────────────────────────────────────────────────────────────┐
│                        SERVICES                               │
│  Orchestrator  •  YeboID  •  YeboSafe  •  Products...        │
│  (Cloud Run, Node.js, TypeScript)                            │
└──────────────────────────────────────────────────────────────┘
                              │
┌──────────────────────────────────────────────────────────────┐
│                          DATA                                 │
│  PostgreSQL (Neon)  •  Redis  •  Cloud Storage               │
└──────────────────────────────────────────────────────────────┘
                              │
┌──────────────────────────────────────────────────────────────┐
│                       EXTERNAL                                │
│  Gemini AI  •  Mobile Money  •  Resend  •  YeboLink          │
└──────────────────────────────────────────────────────────────┘

Core Technologies ​

Backend ​

TechnologyPurposeWhy
Node.js 22RuntimeFast, async, huge ecosystem
TypeScriptLanguageType safety, better DX
ExpressFrameworkSimple, flexible, proven
Prisma 7ORMType-safe DB access

Database ​

TechnologyPurposeWhy
Neon PostgreSQLPrimary DBServerless, scales to zero, branching
RedisCache, SessionsFast, pub/sub, queues
Cloud StorageFilesImages, documents

AI ​

TechnologyPurposeWhy
Google Gemini 2.5 FlashIntent recognition, generationFast, capable, good pricing
EmbeddingsSemantic searchMatching, recommendations

Frontend ​

TechnologyPurposeWhy
React 19Web UIComponent model, ecosystem
ViteBuild toolFast, modern
Tailwind CSS 3StylingUtility-first, rapid
React NativeMobile (future)Code sharing

Infrastructure ​

TechnologyPurposeWhy
Cloud RunContainersServerless, scales to zero
Cloud TasksQueuesAsync job processing
Cloud SchedulerCronScheduled tasks
CloudflareCDN, DNS, WAFPerformance, security
GCP Secret ManagerSecretsSecure credential storage

External Services ​

ServicePurpose
Yebo Chat AppPrimary user interface (app.hiyebo.com)
M-Pesa (Daraja)Kenya mobile money
MTN MoMoWest Africa mobile money
ResendEmail delivery
YeboLinkSMS (our own product)

Service Architecture ​

Monorepo Structure ​

yebo/
├── packages/
│   ├── shared/           # Shared utilities
│   │   ├── types/        # TypeScript types
│   │   ├── utils/        # Common functions
│   │   └── config/       # Shared config
│   │
│   ├── orchestrator/     # Agent brain
│   │   ├── src/
│   │   │   ├── intents/  # Intent handlers
│   │   │   ├── router/   # Product routing
│   │   │   ├── context/  # State management
│   │   │   └── adapters/ # Product adapters
│   │   └── package.json
│   │
│   ├── yeboid/           # Identity service
│   │   ├── api/
│   │   ├── sdk/
│   │   └── package.json
│   │
│   ├── yebosafe/         # Payment service
│   │   ├── api/
│   │   └── package.json
│   │
│   ├── yeboshops/        # Commerce service
│   │   ├── api/
│   │   └── package.json
│   │
│   └── ...               # Other products
│
├── apps/
│   ├── web/              # Main web app
│   ├── hub/              # YeboID hub
│   └── admin/            # Admin dashboard
│
├── docs/                 # Documentation
├── scripts/              # Build, deploy scripts
└── package.json          # Workspace root

Service Communication ​

javascript
// Services communicate via HTTP + events

// Synchronous (HTTP)
const user = await yeboid.getUser(userId);
const wallet = await yebosafe.getWallet(userId);

// Asynchronous (Pub/Sub or webhooks)
eventBus.publish('payment.completed', { orderId, amount });

eventBus.subscribe('payment.completed', async (event) => {
  await yeboshops.updateOrder(event.orderId, 'paid');
  await notify(event.userId, 'Payment received!');
});

Database Design ​

Per-Service Databases ​

Each service owns its data:

Neon Projects:
├── yeboid-db      (users, sessions, handles)
├── yebosafe-db    (wallets, transactions, escrows)
├── yeboshops-db   (listings, orders, reviews)
├── yebojobs-db    (jobs, applications, profiles)
├── yebolearn-db   (courses, enrollments, progress)
└── orchestrator-db (context, tasks, flows)

Cross-Service References ​

Services reference each other by ID:

sql
-- In YeboShops
CREATE TABLE users (
  id UUID PRIMARY KEY,
  yeboid_user_id UUID NOT NULL,  -- Reference to YeboID
  -- product-specific fields
);

-- In YeboSafe
CREATE TABLE wallets (
  id UUID PRIMARY KEY,
  yeboid_user_id UUID NOT NULL,  -- Reference to YeboID
  -- payment-specific fields
);

Data Consistency ​

javascript
// Eventual consistency via events
async function createOrder(buyerId, listingId) {
  // 1. Create escrow in YeboSafe
  const escrow = await yebosafe.createEscrow(...);
  
  // 2. Create order in YeboShops
  const order = await db.orders.create({
    ...orderData,
    escrowId: escrow.id
  });
  
  // 3. Publish event
  await eventBus.publish('order.created', { orderId: order.id });
  
  return order;
}

AI Architecture ​

Intent Recognition ​

javascript
const intentPrompt = `
You are a Yebo intent classifier. Classify the user message into one of these intents:

SELL - User wants to sell something
BUY - User wants to buy something
FIND_JOB - User wants to find a job
APPLY_JOB - User wants to apply to a specific job
SEND_MONEY - User wants to transfer money
CHECK_BALANCE - User wants to check their balance
SEND_INVOICE - User wants to create/send an invoice
LEARN - User wants to learn something
SOURCE - User wants to source products from China
HELP - User needs help
OTHER - None of the above

Also extract any relevant entities (item, price, job title, etc.)

User message: "${message}"

Respond in JSON: { "intent": "...", "entities": {...}, "confidence": 0.0-1.0 }
`;

const response = await gemini.generate(intentPrompt);
const { intent, entities, confidence } = JSON.parse(response);

Context-Aware Responses ​

javascript
async function generateResponse(intent, context, result) {
  const prompt = `
You are Yebo, an AI assistant for economic activities in Africa.
You help users sell, find jobs, learn, and manage money.

User: ${context.handle}
Current task: ${intent.type}
Result: ${JSON.stringify(result)}

Generate a helpful, concise response in a friendly tone.
Include any relevant actions the user can take next.
`;

  return await gemini.generate(prompt);
}

Listing Generation ​

javascript
async function generateListing(photos) {
  // 1. Analyze images
  const analysis = await gemini.analyzeImages(photos, {
    prompt: `
      Analyze these product photos and extract:
      - What is the item?
      - What condition is it in?
      - Any brand or model visible?
      - Suggested category
      - Estimated price range (in KES)
    `
  });
  
  // 2. Generate listing
  const listing = await gemini.generate(`
    Create a marketplace listing based on this analysis:
    ${JSON.stringify(analysis)}
    
    Generate:
    - Title (max 60 chars)
    - Description (max 500 chars)
    - Suggested price
    - Category
  `);
  
  return JSON.parse(listing);
}

Security ​

Authentication ​

javascript
// JWT token structure
{
  "userId": "uuid",
  "handle": "laslie",
  "verified": true,
  "iat": 1710770000,
  "exp": 1710770900  // 15 min
}

// Token validation (in any service)
function validateToken(token) {
  return jwt.verify(token, process.env.YEBOID_JWT_SECRET);
}

Rate Limiting ​

javascript
const rateLimits = {
  'auth/otp/send': { points: 3, duration: 3600 },     // 3/hour
  'auth/signin': { points: 5, duration: 900 },        // 5/15min
  'api/*': { points: 100, duration: 60 },             // 100/min
};

const rateLimiter = new RateLimiterRedis({
  storeClient: redis,
  ...rateLimits[endpoint]
});

Input Validation ​

javascript
// Zod schemas for all inputs
const signupSchema = z.object({
  phone: z.string().regex(/^\+\d{10,15}$/),
  pin: z.string().regex(/^\d{4,6}$/),
  handle: z.string().min(3).max(30).regex(/^[a-z0-9_]+$/)
});

// Validate before processing
const data = signupSchema.parse(req.body);

Deployment ​

CI/CD Pipeline ​

yaml
# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '22'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests
        run: npm test
      
      - name: Build
        run: npm run build
      
      - name: Deploy to Cloud Run
        uses: google-github-actions/deploy-cloudrun@v2
        with:
          service: yebo-${{ matrix.service }}
          region: europe-west1
          image: gcr.io/${{ secrets.GCP_PROJECT }}/${{ matrix.service }}

Environment Configuration ​

env
# Production
NODE_ENV=production
DATABASE_URL=postgresql://...
REDIS_URL=redis://...
YEBOID_JWT_SECRET=...
GEMINI_API_KEY=...
MPESA_CONSUMER_KEY=...
MPESA_CONSUMER_SECRET=...

Monitoring ​

ToolPurpose
Cloud LoggingLogs
Cloud MonitoringMetrics
SentryError tracking
Uptime RobotAvailability

Cost Estimates ​

Per 1,000 Users/Month ​

ServiceCost
Cloud Run$20
Neon PostgreSQL$20
Redis$10
Gemini API$15
WhatsApp API$30
Storage$5
Total$100

Cost per user: $0.10/month


Technology Decisions Log ​

DecisionChosenAlternativesWhy
LanguageTypeScriptPython, GoType safety, JS ecosystem
DatabaseNeonPlanetScale, SupabaseServerless, branching
AIGeminiOpenAI, ClaudeSpeed, pricing, capabilities
HostingCloud RunVercel, RailwayGCP ecosystem, scaling
CacheRedisMemcachedPub/sub, queues

Previous: Phase 4: Scale

One chat. Everything done.