Technical Documentation
Complete architecture, formulas, and system reference for startfrom.today
1. Architecture Overview
- Frontend: React 18 + Vite + TypeScript + Tailwind CSS + shadcn/ui
- State: TanStack React Query (staleTime 5 min, gcTime 30 min)
- Backend: own API at
api.startfrom.today— Node.js (Fastify), Postgres 16 + Apache AGE (skill graph), MinIO object storage, Caddy; deployed on DigitalOcean - AI: Anthropic Claude via the Anthropic API (extraction, classification, matching — no user API key needed)
- MCP: first-party MCP server at
api.startfrom.today/v1/mcpwith its own OAuth 2.1 authorization server - Visualisation: D3.js (force-directed graph, sunburst), Recharts (bar/radar charts)
2. Database Schema (23 tables)
| Table | Purpose | Key Columns |
|---|---|---|
| profiles | User metadata & settings | user_id, full_name, onboarding_completed, dashboard_layout, public_widgets |
| user_skills | Aggregated skill hours | user_id, skill_name, hours |
| user_experiences | Work history entries | user_id, title, company, start_date, end_date, description |
| user_education | Education entries | user_id, degree, institution, start_date, end_date |
| experience_skills | Skills linked to experience | experience_id → user_experiences, skill_name, weight |
| education_skills | Skills linked to education | education_id → user_education, skill_name, weight |
| user_actions | Daily learning actions | user_id, description, duration_minutes, action_date |
| action_skills | Skills linked to actions | action_id → user_actions, skill_name |
| user_books | Books read by user | user_id, title, author, pages, isbn |
| book_skills | Skills linked to books | book_id → user_books, skill_name |
| skill_classifications | AI-assigned category + EQF level | user_id, skill_name, category, eqf_level |
| ai_cache | Per-user AI response cache (7-day TTL) | user_id, cache_type, cache_key, response, expires_at |
| global_ai_cache | Shared AI response cache | cache_type, cache_key, response |
| ai_agents | AI tool directory | name, category, description, url, pricing, active |
| esco_occupations | ESCO occupation taxonomy | id, title, isco_code, uri |
| esco_skills | ESCO skill taxonomy | id, title, skill_type, uri |
| esco_occupation_skills | Occupation ↔ skill mapping | occupation_id, skill_id, relation_type |
| user_connections | Friendship links (bidirectional) | user_id, friend_id, invitation_id |
| friend_requests | Pending friend requests | sender_id, receiver_id, status |
| messages | Direct messages between friends | sender_id, receiver_id, content, read |
| invitations | Invite codes | inviter_id, invite_code |
| user_roles | RBAC roles | user_id, role (admin | moderator | user) |
| analytics_events | Page-view telemetry | user_id, page, event_type, metadata |
| family_kids | Children linked to a parent profile | user_id, name, birth_date |
| kid_education_tracks | Education pathways planned per kid | kid_id → family_kids, track_type, label |
| kid_track_years | Year-by-year breakdown of a kid's track | track_id → kid_education_tracks, year, focus |
| kid_passports | Citizenships/passports stored per kid | kid_id → family_kids, country, number |
| spouse_links | Spouse relationships between profiles | user_id, spouse_user_id |
| profile_discovery | Discoverable public profile index | user_id, display_name, mastery |
3. Skill Hours Calculation
Hours are computed per calendar month for each experience/education entry that overlaps that month.
Where 167 = average working hours per month (2000 / 12).
The monthly hours are then distributed across skills by weight:
Action hours are added directly: duration_minutes / 60. Books contribute pages × 3 / 60 hours (3 min/page estimate), split equally across book skills.
4. Mapped Percentage
Denominator = 12 fields × 8 depth levels, with per-level capacities of 50 / 150 / 400 / 800 / 1,500 / 3,000 / 6,000 / 10,000 h. Hours above a cell's capacity still count as hours but stop moving the percentage. See the methodology for the reasoning.
5. Depth Level Classification
Each skill is classified into one of 12 domain categories and assigned a depth level (1–8) by the AI classification engine (Gemini model). The classification considers:
- Skill name and context from linked experiences/education
- Complexity and specialisation indicators
- Alignment with the EQF 1–8 depth descriptors used internally
| EQF | Label | Description |
|---|---|---|
| 1 | Basic | General knowledge, simple tasks |
| 2 | Elementary | Basic factual knowledge, routine work |
| 3 | Intermediate | Broad knowledge, range of cognitive skills |
| 4 | Upper-Intermediate | Factual & theoretical in broad contexts |
| 5 | Advanced | Comprehensive, specialised knowledge |
| 6 | Professional | Advanced knowledge, critical understanding |
| 7 | Expert | Highly specialised, research-level |
| 8 | Pioneer | Most advanced frontier of a field |
6. Earning Potential Model
Salary estimation matches user skills against occupation salary ranges, adjusted by seniority:
Where:
low, high— salary range from the matched occupationseniority— capped at 1.0, computed from merged experience years + advanced degree bonus:
Experience multiplier tiers:
- ≤ 2 years → 0
- 2–5 years → 0.3
- 5–10 years → 0.6
- > 10 years → 0.85
Top 3 matching occupations are weighted by skill match percentage to produce a blended estimate. Experience intervals are merged to avoid double-counting overlapping roles.
Pareto analysis: Identifies the smallest set of skills that drive 80% of earning potential, weighted by how many top roles each skill matches.
7. Automation Risk Score
Each skill receives an automation probability (0–1) from the AI engine. The overall risk is a weighted average where skills with more invested hours have proportionally more influence.
8. Skill Graph (D3 Force-Directed)
The interactive skill graph uses D3's force simulation with these parameters:
- Center force: pulls all nodes toward viewport center
- Charge force:
d3.forceManyBody().strength(-200)— repulsion between nodes - Link force: connects skill nodes to their domain category nodes
- Node radius: proportional to
sqrt(hours) - Colour mapping: 12 category colours assigned via ordinal scale
- Recommended nodes: Future-Proof skill recommendations appear as red "Recommended" nodes connected to relevant domain categories
9. Sunburst Chart
Hierarchical ring chart built with d3.partition(). Three levels:
- Center: total skill hours
- Inner ring: 12 domain categories (sized by sum of hours)
- Outer ring: individual skills within each category
10. API & AI Pipelines
| Function | Purpose | AI Model |
|---|---|---|
| /v1/ai/classify-skills | Assign category + EQF level to skills | Claude |
| /v1/ai/classify-work-type | Classify skills into work-type quadrants | Claude |
| /v1/ai/describe-skill | Generate skill descriptions (single & batch) | Claude |
| /v1/ai/recommend-skills | Suggest new skills based on profile | Claude |
| /v1/ai/automation-risk | Score automation resistance per skill | Claude |
| /v1/ai/profile-summary | AI-written profile summary | Claude |
| /v1/ai/parse-cv | Extract structured data from uploaded CVs | Claude |
| /v1/ai/extract-action-skills | Extract skills from action descriptions | Claude |
| /v1/ai/extract-book-skills | Extract skills from book metadata | Claude |
| /v1/ai/analyze-job | Match a job posting against your record | Claude |
| /v1/skills · /v1/actions · /v1/books | Record CRUD + hours recalculation | — |
| /v1/me/* · /v1/profile/* | Profile core, experiences, education | — |
| /v1/network · /v1/messages · /v1/family · /v1/streaks | Social contour | — |
| /v1/esco/* | Public ESCO taxonomy (occupations, skills) | — |
| /v1/storage/* · /v1/files/* | Avatars and CV archive (MinIO) | — |
| /v1/billing/* | Stripe checkout, portal, webhooks | — |
| /v1/auth/* · /v1/oauth/* | Own auth + OAuth 2.1 AS for MCP | — |
11. AI Caching Strategy
- Per-user cache (
ai_cache): keyed bycache_type + cache_key, expires after 7 days. Used for user-specific results (recommendations, summaries, work-type classifications). - Global cache (
global_ai_cache): shared across users. Used for skill descriptions, automation risk scores per skill, and other non-personalised data. - Client-side: React Query with
staleTime: Infinityfor AI-cached data (never refetches within a session) and standard stale times for user data.
12. Dashboard System
The dashboard consists of 19 draggable "plates" whose order is persisted in profiles.dashboard_layout as a JSON array. AI-powered plates display real-time skill counts and previews fetched from cached AI responses.
- Plates: Skills & Hours, All Human Knowledge (Mastery %), Earning Potential, Pareto Skills, Automation Risk, Skill Graph, Future-Proof Skills, Recommended Skills, Work-Life Balance, Achievements, Books, Invite Friends, High-Value Skills, Diagram, Total Skills, Total Hours, and 4 Work-Type Quadrant plates (Routine/Non-Routine × Intellectual/Physical)
- Layout is drag-reorderable and saved on drop
- Each plate can be toggled for public profile visibility via
profiles.public_widgets - Enriched plates: Automation Risk shows top 3 highest and bottom 3 lowest risk skills; Future-Proof shows avg risk and matching occupations; Earning Potential shows seniority-adjusted estimate with top 3 matched roles; Invite Friends shows side-by-side sphere comparison
13. Authentication & Security
- Own authentication: Sign in with Apple, Google, and email/password (argon2id hashing)
- ES256-signed JWT access tokens + refresh-token rotation with reuse detection
- The database is private — every read and write goes through the API, which enforces per-account access on each request (no client-side database access)
- Friend-scoped reads (skills, sphere, experiences) require an accepted connection, checked server-side
- MCP clients authorize through our OAuth 2.1 authorization server (dynamic client registration, mandatory PKCE, user consent page)
- RBAC via
user_rolestable (admin-only endpoints)
14. Public Profile Sharing
Users can share a public profile URL. Visible widgets are controlled by the public_widgets JSON column. Public profile data is fetched using service-role RPC to bypass RLS while still respecting the widget visibility settings.
15. Skill Name Formatting & Deduplication
- Title Case: All skill names are formatted in Title Case across the platform using a shared
toTitleCase()utility - Deduplication: Case-insensitive deduplication is applied in the Groups view and skill recommendations to prevent duplicate entries
- Consistency: Formatting is applied at the display layer — stored skill names remain as-is for backward compatibility
16. Invitation & Network System
- Invite link:
https://startfrom.today/invite/:code - Default message: "I know (XX)% of all human knowledge, check out yours" — where XX is the user's mastery percentage
- Friend requests: Acceptance creates bidirectional
user_connectionsentries - Network page: Three tabs — Friends (with chat links), Discover (with search), Invite (shareable link + custom message)
- Direct messaging: Friends can exchange messages via the Messages page with real-time read receipts
17. Strategic Skill Recommendations
- Future-Proof Skills: Skills absent from user profile, with automation risk <40%, found in occupations matching >15% of existing skills. Dashboard plate previews top 3 lowest-risk skills with average risk score and occupation unlocks. Full list at
/dashboard/future-proof-skills - You Might Also Know: AI-suggested skills based on co-occurrence, prerequisites & domain patterns. Dashboard plate shows up to 4 example skill names. Full list at
/dashboard/recommended-skills - Graph integration: Future-Proof recommendations appear as red "Recommended" nodes in the Skill Graph
- Course generation: The personalised learning course is generated exclusively from Future-Proof skills
18. Knowledge Sphere & Compare
- Sphere view: Sunburst chart visualising mastery across 12 ESCO categories × 8 ISCED levels (skills from work are levelled with the EQF descriptors)
- Compare mode: Side-by-side sunburst comparison with connected friends, toggled via Sphere/Compare switch
- Skills views: List, Graph, and Groups tabs — Groups organise skills exclusively by ESCO category
19. Action Editing
Logged daily actions are fully editable inline. Users can modify any action after creation without needing to delete and re-create it.
- Duration: Editable numeric input — changes are saved to
user_actions.duration_minutesvia UPDATE policy - Date: Calendar date picker (shadcn) — future dates are disabled. Updates
user_actions.action_date - Remove skills: Each skill badge shows an × button in edit mode. Removing a skill deletes the
action_skillsrow and subtracts the proportional hours fromuser_skills - Add skills: Inline text input to add new skills. Inserts into
action_skillsand upserts hours intouser_skills - Hour redistribution: When skills are added or removed, the action's duration is redistributed proportionally across the updated skill set
- RLS: UPDATE policy on
user_actionsensures only the owner can edit their actions