8.1 KiB
8.1 KiB
Passport Architecture: Foundation as Sole Issuer
Overview
aethex.foundation is the sole issuer and authority for all user passports/identities in the AeThex ecosystem. All other platforms (aethex.dev, future platforms) are OAuth clients that consume Foundation-issued identities.
This document defines the passport architecture and data flow.
Core Principle
Foundation = Single Source of Truth (SSOT) for identity
Each Platform = Read-only cache of Foundation passport data
Architecture
1. Foundation (aethex.foundation)
- Owner of passport data
- Issues and maintains all user identities
- Provides OAuth endpoints for authentication
- Provides passport API endpoints for clients to fetch user data
- Single authority for all identity mutations (updates, deletions)
2. Client Platforms (aethex.dev, etc.)
- Consumers of Foundation identities
- Cache Foundation passport data locally for performance
- Use cached data for reads (lookups, profile queries)
- Forward any profile updates to Foundation's API
- Validate passport integrity on each login
Data Flow
User Login Flow
User → Login → Redirect to Foundation
↓
Foundation (OAuth) → Verify credentials
↓
Return access_token + user data to aethex.dev
↓
aethex.dev (callback handler):
1. Exchange code for token with Foundation
2. Fetch userinfo from Foundation API
3. Validate passport is complete
4. SYNC (one-way) to local cache:
- User profile data (from Foundation)
- Achievements (from Foundation)
- Projects (from Foundation)
5. Create session on aethex.dev
↓
User authenticated, using Foundation passport
Profile Update Flow
User edits profile on aethex.dev
↓
aethex.dev validates input locally
↓
Forward update request to Foundation API
↓
Foundation (SSOT) validates and applies changes
↓
Foundation returns updated passport
↓
aethex.dev syncs updated data to local cache
↓
Update confirmed to user
Profile Read Flow
User/App requests profile on aethex.dev
↓
Check local cache
↓
If cache valid: Return cached data
If cache stale: Refresh from Foundation → update cache → return
↓
Never modify local cache outside of sync operations
Key Rules
✅ ALLOWED Operations on aethex.dev
- Read from local cache - Fast lookups of passport data
- Sync from Foundation - One-way data import during login/refresh
- Validate against cache - Use cached data for access control
- Forward to Foundation - Route update requests to Foundation API
- Cache invalidation - Clear stale cache on logout or explicit refresh
❌ FORBIDDEN Operations on aethex.dev
- Direct writes to passport data - All identity mutations must go through Foundation
- Creating new passports - Only Foundation can issue identities
- Modifying cached data - Cache is read-only except during sync
- Trusting unvalidated data - Always verify data came from Foundation
Database Schema (aethex.dev)
-- Local cache of Foundation passport data
CREATE TABLE user_profiles (
id UUID PRIMARY KEY, -- Foundation user ID (immutable)
email TEXT UNIQUE NOT NULL, -- From Foundation
username TEXT UNIQUE, -- From Foundation
full_name TEXT, -- From Foundation
avatar_url TEXT, -- From Foundation
profile_completed BOOLEAN DEFAULT false,-- From Foundation
-- Sync tracking
foundation_synced_at TIMESTAMP, -- When last synced from Foundation
cache_valid_until TIMESTAMP, -- When cache expires
-- Local metadata (not from Foundation)
last_login_at TIMESTAMP, -- aethex.dev tracking only
created_at TIMESTAMP,
updated_at TIMESTAMP
);
-- Data from Foundation that shouldn't be written locally
-- (achievements, projects, etc. are fetched on-demand)
Sync Mechanism
Initial Sync (On Login)
- User authenticates with Foundation
- aethex.dev receives
access_tokenand basic user info - aethex.dev calls Foundation's
/api/oauth/userinfoendpoint - All user data from Foundation gets UPSERTED to local cache
foundation_synced_attimestamp is updated
Periodic Sync (Background)
Every 24 hours (or on explicit request):
1. Fetch current user data from Foundation API
2. Compare with local cache
3. If Foundation data differs: update cache
4. If local cache is newer: ERROR (shouldn't happen)
5. Update `cache_valid_until` timestamp
Cache Expiration
If (now > cache_valid_until):
1. Treat cache as stale
2. Fetch fresh from Foundation
3. Update cache and expiration
Error Handling
What if Foundation is unavailable?
During Login:
- <EFBFBD><EFBFBD> Cannot proceed - user must authenticate through Foundation
- Return error: "Identity service unavailable"
For existing users (cache valid):
- ✅ Can use cached data temporarily
- Continue serving from cache
- Log warning about Foundation unavailability
- Retry sync in background
For profile updates:
- ❌ Cannot proceed - updates must go through Foundation
- Queue update locally, retry when Foundation is available
- Or fail gracefully: "Identity service unavailable, please try again"
Validation Rules
On Every Auth Request
if (!user.foundation_synced_at) {
throw new Error("User not synced from Foundation");
}
if (now > user.cache_valid_until) {
// Refresh from Foundation
await syncFromFoundation(user.id);
}
// User is authenticated and passport is valid
On Every Profile Update Request
// Forward to Foundation API, never write locally
const updated = await foundation.api.updateProfile(userId, changes);
// On success, sync response back to cache
await syncFromFoundation(userId);
return updated;
Migration from Old Model
Old Model: aethex.dev wrote directly to passport table New Model: aethex.dev only syncs FROM Foundation
Migration Steps
- ✅ Remove Foundation arm from aethex.dev UI (DONE)
- ✅ Clarify that aethex.dev is an OAuth client of Foundation (DONE)
- Make profile endpoints read-only, forward writes to Foundation
- Add cache validation on every auth request
- Implement background sync for cache freshness
- Monitor logs for any direct write attempts (should be 0)
- Deprecate old direct-write endpoints
API Endpoints Reference
Foundation APIs (Used by aethex.dev)
GET /api/oauth/authorize - Start OAuth flow
POST /api/oauth/token - Exchange code for token
GET /api/oauth/userinfo - Fetch authenticated user's passport
GET /api/users/:id - Fetch specific user passport (public data)
PATCH /api/users/:id - Update passport (requires auth)
aethex.dev APIs (Now read-only/cache-focused)
GET /api/profile/:username - Read from cache or fetch from Foundation
POST /api/profile/ensure - Sync passport from Foundation (on login)
PATCH /api/profile/:id - Forward to Foundation (no local writes)
Monitoring & Observability
Log these events
[Passport Sync] User synced from Foundation: {userId}
[Passport Error] User not found in Foundation: {userId}
[Passport Stale] Cache expired for user: {userId}, refreshing...
[Passport Write] Attempted direct write to local cache (FORBIDDEN): {userId}
[Passport Conflict] Local cache newer than Foundation (SYNC ERROR): {userId}
Metrics to track
- Sync success rate (should be ~100%)
- Cache hit rate (should be >95% for logged-in users)
- Foundation API latency
- Failed sync attempts
- Stale cache detections
Future Enhancements
- Webhook sync - Foundation notifies clients when passport changes
- Event stream - Subscribe to passport update events
- Multi-version support - Handle different Foundation versions
- Distributed cache - Redis layer for cross-instance consistency
- Audit trail - Track all passport mutations at Foundation level
Related Documentation
code/docs/PHASE3-SWITCHOVER-GUIDE.md- OAuth migration guidecode/api/auth/callback.ts- Login sync implementationcode/api/profile/ensure.ts- Profile sync endpoint