$ cat system.md
For the curious. How we're preserving Ge'ez script and East African languages for the next generation.
admin panel
mobile app
web learning app
admin.dependencies
framework
- Next.js14.xApp Router, RSC, middleware
- React18.xServer + client components
- TypeScriptstrictEnd-to-end type safety
data
- PostgreSQL/Neon—Serverless Postgres, branching
- Prisma5.x18 models, typed queries
- Cloudflare R2S3 APIImages, audio, book assets
api
- tRPC11.x80+ procedures across 12 routers
- Zod3.xRuntime validation, shared schemas
- superjson—Date serialization, never fileData
auth
- Clerk6.xGoogle OAuth, invite-only
- Role system—Admin, Contributor, Author roles
ai
- Google Imagen 4—Text-to-image book illustrations
- Gemini 2.5 Flash—Reference-based style matching
- sharp0.34Resize + WebP optimization
payments
- Stripe—Book purchases, author Connect payouts
- Lulu API—Print-on-demand hardcovers
audio + media
- Web Audio APInativeBrowser recording via MediaRecorder
- @react-pdf/renderer4.xPrint-ready book PDFs
- Resend—Invite emails, feedback notifications
ui
- Tailwindv3Utility-first, dark mode
- Radix/shadcn—Accessible primitives
- Lucide icons—Tree-shakeable
infra
- VercelEdge+NodeZero-config deploys from master
- GA4 G-tag—via @next/third-parties
mobile.dependencies
mobile framework
- ExpoSDK 54Managed workflow, OTA updates
- React Native0.81New Architecture ready
- React19.xNew compiler
- Expo Routerv6File-based navigation
graphics
- Skia@shopifyGPU-accelerated 2D canvas for particles
- Reanimated4.x60fps gesture-driven animations
- react-native-svg15.xStroke tracing canvas
mobile audio
- expo-audio1.xPlayback with music ducking
storage
- expo-sqlite16.xOffline progress, content cache
payments
- RevenueCat9.xTip jar — coffee, injera, feast
observability
- Sentry7.xCrash reporting, source maps
- expo-updates29.xOver-the-air updates
web.dependencies
web client
- IndexedDBnativeContent cache, progress, ETag tracking
- Web Audio APInativeHybrid AudioContext + HTMLAudioElement
- HTML5 CanvasnativeStroke tracing with accuracy zones
shared code
- @fidel/sharedworkspacenpm workspace, types + game logic + i18n strings
- content-sync.ts—ETag-cached REST consumer
- progress.ts—4 IndexedDB stores
web games
- Matching—Drag-to-match vocab
- Sound Quiz—Audio-first identification
- Bubble Pop—Tap-to-pop timed
- Memory Pairs—Flip-to-match
architecture
storage: r2 + postgresBook assets (illustrations, covers, narration, music) in Cloudflare R2. Legacy audio as Postgres bytea. sharp optimizes all images to WebP before upload.
api: trpc + zod12 routers, 80+ procedures. Input schemas defined once, shared between client and server. superjson for serialization — but never serialize fileData (breaks it).
auth: invite-firstNo self-registration. Admin invites users via UI with role assignment (Admin/Contributor/Author). UserInvite consumed on first login. Falls back to ADMIN_EMAILS env.
ai: imagen + geminiBook illustrations via Google Imagen 4 (text-to-image) or Gemini 2.5 Flash (reference-based). 5 prompt templates with auto-filled variables. House style prefix ensures consistency.
books: author workflowDRAFT → SUBMITTED → IN_REVIEW → APPROVED → PUBLISHED. Multilingual text + narration + music per page. Stripe payments for purchases, Connect for author payouts.
print: lulu podPrint-on-demand hardcovers via Lulu API. PDF generation with @react-pdf/renderer + Fidel Family Sans. Inventory tracking with stock vs print-on-demand fulfillment.
sync: etag-cached RESTMobile app polls /api/v1/content/ endpoints. ETag + If-None-Match headers for bandwidth-efficient change detection. 22 API routes, all public content GET-only.
canvas: stroke-editorAdmin draws Ge’ez letter strokes on HTML canvas. Stroke data saved as JSON per form. 33 consonants × 7 forms = 231 stroke sequences.
i18n: multi-languageFour languages: Tigrinya, Amharic, English, and Oromo. All content — packs, fidel, books, locale — fully translated. Audio in ti/am/en; Oromo audio in progress.
offline: bundled + syncedAll content ships in the APK. SQLite cache fills on first sync. Admin API merges overrides in background. Network failures never block the app.
render: skia particlesGPU-accelerated particle effects via @shopify/react-native-skia. Confetti, fireflies, celebration bursts. Error boundary silently disables on unsupported devices.
privacy: coppa-compliantZero PII stored. No analytics, no ads, no social features. Progress tracked locally in SQLite only. Parental gate (math CAPTCHA) for settings.
content.system
Vocabulary Packs
N packs × 10 items × 4 languages
Animals, Family, Body Parts, Foods, and more. Each item has names and pronunciation guides in ti/am/en/om.
Fidel (Ge’ez Script)
33 consonants × 7 vowel forms × 4 languages
Full Ge’ez syllabary. Each form has character, names, pronunciation, stroke data, and audio in all four languages.
Children’s Books
Multilingual pages × AI illustrations
Picture books with text in ti/am/en/om, AI-generated illustrations (Imagen 4 + Gemini), narration audio, background music. Free or paid via Stripe.
Locale Strings
N keys × 4 languages
UI strings for the mobile app. Key-value pairs in en/ti/am/om. Admin-editable, versioned, served via content API.
recording.lifecycle()
Browser mic via MediaRecorder API. Space bar to start/stop. Preview before saving.
Uploaded to Cloudflare R2 with fallback to Postgres bytea. Status: PENDING. MIME type preserved.
Queue-based review at /admin/recordings. Play, compare, approve or reject with notes.
Approved → isActive: true. Previous active recording for same item+language → isActive: false.
GET /api/v1/content/packs returns only isActive recordings. ETag caching minimizes bandwidth.
api.v1.content
| path | method | cache | purpose |
|---|---|---|---|
| /api/v1/content/manifest | GET | ETag | Content hashes for change detection. Mobile checks this first. |
| /api/v1/content/packs | GET | ETag | All packs + items + approved audio URLs. |
| /api/v1/content/fidel | GET | ETag | Consonants + vowel forms + stroke data + audio. |
| /api/v1/content/locale | GET | ETag | UI strings grouped by language (en/ti/am/om). |
| /api/v1/content/books | GET | ETag | Published book catalog with pricing and metadata. |
| /api/v1/content/books/[id] | GET | ETag | Book pages, illustrations, narration, music. |
| /api/v1/content/announcements | GET | ETag | In-app event announcements with illustrations. |
| /api/v1/feedback | POST | — | User feedback with device info. Triggers admin email. |
// All endpoints public. GET-only. CORS enabled for mobile app.
// 304 Not Modified when ETag matches. Zero bandwidth wasted.
mobile --verbose
140px touch targets, audio-first design, no text-only content. High contrast watercolor illustrations replace emoji.
Bundled content loads instantly. SQLite caches API responses + ETags. Background sync merges updates. Network failures are invisible.
Landscape-locked competitive game for twins sharing a tablet. Separate score tracking, turn-based play, winner celebration.
GPU-accelerated confetti, fireflies, celebration bursts, ink trails. 6 presets with graceful degradation on unsupported devices.
Draw Ge’ez characters on canvas. Zone-based accuracy validation (55% threshold). Multi-stroke support with real-time feedback.
Semay & Miriam — twin guides with 11 poses each (WebP optimized). Contextual reactions: clap on correct, encourage on retry.
Math CAPTCHA locks settings. RevenueCat tip jar (3 tiers: $2.99/$4.99/$9.99). Supporter badge on dashboard. Zero pressure.
web --verbose
IndexedDB caches all packs, fidel, books, announcements with ETag change detection. First load fetches from API, subsequent loads serve from cache.
Matching, Sound Quiz, Bubble Pop, Memory Pairs. All games use GameShell wrapper with score tracking, celebration confetti, and progress recording.
HTML5 Canvas for drawing Ge’ez characters. Zone-based accuracy validation from shared stroke data. Visual feedback with color-coded accuracy.
Page-by-page reader with cover, narration audio, background music, and language switching. Supports free and paid books with purchase verification.
Math CAPTCHA (addition/subtraction) protects parent dashboard and settings. Session-based unlock via sessionStorage.
Middleware rewrites app.fidel.family to (learning) route group. Same Next.js app serves admin, marketing, store, and learning on different subdomains.
fidel.strokes
admin: stroke-editor
Admin draws letter strokes on an HTML5 canvas. Stroke order, direction, and control points saved as JSON per Fidel form. 33 consonants × 7 forms = 231 stroke sequences to record.
mobile: stroke-tracing
Children trace Ge'ez characters on a react-native-svg canvas. Zone-based accuracy validation — 55% of points must land in the center zone to accept. Multi-stroke support with real-time feedback and celebration particles on success.
security
clerkMiddleware()Every protected route requires valid Clerk session. Role-based tRPC procedures: adminProcedure, authorProcedure, protectedProcedure. Zod validates all inputs.
auth: invite-firstNo self-registration. Admins invite users via UI with role (Admin/Contributor/Author). UserInvite consumed on first login. Email from Clerk server-side — never client input.
payments: stripe webhooksBook purchases via Stripe PaymentIntent. Webhook signature verification for payment_intent.succeeded and charge.refunded. Lulu webhooks HMAC-verified for print status.
cors: read-only APIPublic /api/v1/ endpoints allow GET only. CORS headers permit cross-origin reads for the mobile app. Purchase/download mutations require Clerk auth.
coppa: zero PIIMobile app stores no personally identifiable information. No analytics, no ads, no social features. Progress tracked locally in SQLite only. Designed for children under 13.
principles
- #Every child deserves to hear their language. Ship audio, not excuses.
- #Offline-first, always. Network failures are invisible to a toddler.
- #Contributors record, admins review, authors publish. Trust the workflow.
- #Four languages: ti/am/en/om. Every word, every book, every page.
- #AI illustrates, humans curate. Imagen generates, authors approve.
- #The mobile app is the product. The admin panel is the factory.
- #No ads, no tracking, no social. COPPA compliance is a feature, not a constraint.
- #From screen to hardcover. Print-on-demand means every book is real.
$ fidel-family --version
Built to preserve Ge'ez script and East African languages for the next generation.
Three apps. 1,000+ audio slots. AI-illustrated books. Four languages. One mission.
$ _
All this tech serves one goal: every child hears their language.
web + mobile + admin · 1,000+ audio slots · 4 languages · 33 Ge'ez consonants · AI-illustrated books · print-on-demand
invite-only · contributor, author & admin accounts