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
| Technology | Purpose | Why |
|---|---|---|
| Node.js 22 | Runtime | Fast, async, huge ecosystem |
| TypeScript | Language | Type safety, better DX |
| Express | Framework | Simple, flexible, proven |
| Prisma 7 | ORM | Type-safe DB access |
Database
| Technology | Purpose | Why |
|---|---|---|
| Neon PostgreSQL | Primary DB | Serverless, scales to zero, branching |
| Redis | Cache, Sessions | Fast, pub/sub, queues |
| Cloud Storage | Files | Images, documents |
AI
| Technology | Purpose | Why |
|---|---|---|
| Google Gemini 2.5 Flash | Intent recognition, generation | Fast, capable, good pricing |
| Embeddings | Semantic search | Matching, recommendations |
Frontend
| Technology | Purpose | Why |
|---|---|---|
| React 19 | Web UI | Component model, ecosystem |
| Vite | Build tool | Fast, modern |
| Tailwind CSS 3 | Styling | Utility-first, rapid |
| React Native | Mobile (future) | Code sharing |
Infrastructure
| Technology | Purpose | Why |
|---|---|---|
| Cloud Run | Containers | Serverless, scales to zero |
| Cloud Tasks | Queues | Async job processing |
| Cloud Scheduler | Cron | Scheduled tasks |
| Cloudflare | CDN, DNS, WAF | Performance, security |
| GCP Secret Manager | Secrets | Secure credential storage |
External Services
| Service | Purpose |
|---|---|
| Yebo Chat App | Primary user interface (app.hiyebo.com) |
| M-Pesa (Daraja) | Kenya mobile money |
| MTN MoMo | West Africa mobile money |
| Resend | Email delivery |
| YeboLink | SMS (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 rootService 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
| Tool | Purpose |
|---|---|
| Cloud Logging | Logs |
| Cloud Monitoring | Metrics |
| Sentry | Error tracking |
| Uptime Robot | Availability |
Cost Estimates
Per 1,000 Users/Month
| Service | Cost |
|---|---|
| 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
| Decision | Chosen | Alternatives | Why |
|---|---|---|---|
| Language | TypeScript | Python, Go | Type safety, JS ecosystem |
| Database | Neon | PlanetScale, Supabase | Serverless, branching |
| AI | Gemini | OpenAI, Claude | Speed, pricing, capabilities |
| Hosting | Cloud Run | Vercel, Railway | GCP ecosystem, scaling |
| Cache | Redis | Memcached | Pub/sub, queues |
Previous: Phase 4: Scale