Overview
Platform Connections enable Genie Helper to authenticate with creator platforms for scraping stats and publishing content. Credentials are encrypted with AES-256-GCM and stored in the Directuscreator_profiles collection.
Supported Platforms:
- OnlyFans
- Fansly
- Pornhub
- XVideos
- TikTok
- X (Twitter)
- YouTube
Connection Methods
1. Cookie Capture (Recommended)
Flow:- User logs into platform via browser
- GenieHelper browser extension captures session cookies
- Cookies encrypted and stored in
platform_sessionscollection - Stagehand injects cookies to bypass login screens
- No credential storage required
- Works with 2FA/SSO platforms
- Session reuse reduces bot detection risk
- No password exposure
2. Username/Password (Legacy)
Flow:- User enters credentials in dashboard
/app/platforms - Credentials encrypted with AES-256-GCM
- Stored in
creator_profiles.credentialsfield - Stagehand uses credentials for automated login
- Fails on platforms with 2FA
- Requires password exposure
- Higher bot detection risk
- Manual login automation brittle
- Platforms without 2FA
- Initial setup before cookie capture
- Fallback if cookies expire
3. OAuth (Planned)
Roadmap: Phase 9E Supported Platforms:- Google (YouTube)
- X (Twitter)
- User clicks “Connect with OAuth” in dashboard
- Redirected to platform OAuth consent screen
- Platform issues access token + refresh token
- Tokens encrypted and stored in
creator_profiles.credentials - Dashboard polls for OAuth callback
Credential Storage
Creator Profiles Collection
Collection:creator_profiles
| Field | Type | Description |
|---|---|---|
id | UUID | Primary key |
user_id | M2O (directus_users) | Creator who owns this connection |
platform | String | Platform identifier (e.g., “onlyfans”) |
username | String | Platform username (plaintext) |
credentials | JSON | Encrypted credential object |
scrape_enabled | Boolean | Enable automated scraping |
scrape_frequency | String | Cron expression (e.g., “0 */6 * * *“) |
last_scraped_at | Timestamp | Last successful scrape |
scrape_status | String | idle, running, success, error |
profile_data | JSON | Cached stats (followers, earnings) |
Credentials Field Format
Unencrypted Payload (before encryption):v1:<iv_b64>:<tag_b64>:<ciphertext_b64>
Platform Sessions Collection
Collection:platform_sessions
| Field | Type | Description |
|---|---|---|
id | UUID | Primary key |
creator_profile_id | M2O | Link to creator profile |
platform | String | Platform name (e.g., “onlyfans”) |
cookies | JSON | Encrypted cookie array |
user_agent | String | Browser user agent |
captured_at | Timestamp | Cookie capture time |
expires_at | Timestamp | Estimated expiration (90 days) |
last_used_at | Timestamp | Last Stagehand injection |
Encryption Implementation
AES-256-GCM
Genie Helper uses AES-256-GCM (Galois/Counter Mode) for authenticated encryption: Security Properties:- Confidentiality: Credentials unreadable without key
- Integrity: Tampered ciphertext rejected during decryption
- Authentication: Additional Authenticated Data (AAD) prevents context swapping
- Encryption Key: 32 bytes (256 bits)
- IV (Initialization Vector): 12 bytes (96 bits), random per encryption
- Auth Tag: 16 bytes (128 bits), generated during encryption
- AAD: “agentx-v1” (fixed context string)
Encryption Module
Location:/home/daytona/workspace/source/server/utils/credentialsCrypto.js:1
Functions:
encryptJSON(obj): Encrypts object → returns envelopedecryptJSON(envelope): Decrypts envelope → returns object
Encryption Process
encryptJSON(obj) Flow:
Decryption Process
decryptJSON(envelope) Flow:
Key Generation
To generate a new encryption key:.env:
Dashboard Integration
Connecting a Platform
Page:/app/platforms
Flow:
- User clicks “Add Platform”
- Selects platform from dropdown
- Enters username + password (or clicks “Capture Cookies”)
- Dashboard calls
/api/credentials/store-platform-credentials - Server encrypts credentials via
encryptJSON() - Inserts
creator_profilesrecord with encryptedcredentialsfield - Dashboard shows success + scrape configuration options
dashboard/src/pages/PlatformConnect/index.jsx):
Credentials API
Endpoint:/api/credentials/store-platform-credentials
Location: /home/daytona/workspace/source/server/endpoints/api/credentials.js:1
Request:
Scraping Configuration
Enabling Auto-Scrape
After connecting a platform, configure automated scraping: Dashboard UI:- Scrape Enabled: Toggle to enable/disable
- Frequency: Cron expression or preset (every 6 hours, daily, etc.)
- Last Scraped: Timestamp of last successful scrape
- Status:
idle,running,success,error
Cron Expressions
Common Patterns:| Expression | Meaning |
|---|---|
0 */6 * * * | Every 6 hours |
0 0 * * * | Daily at midnight |
0 */12 * * * | Every 12 hours |
0 0 * * 0 | Weekly on Sunday |
0 0 1 * * | Monthly on 1st |
post_scheduler worker (media-worker) polls creator_profiles for enabled scrapes.
Implementation (media-worker/index.js):
Manual Scraping
Trigger from Dashboard
Button: “Scrape Now” on/app/dashboard
Flow:
- User clicks “Scrape Now”
- Dashboard checks for valid
platform_sessionsrecord - If no cookies: creates
hitl_sessionsrecord → shows yellow banner - If cookies exist: enqueues
scrape_profilejob via/api/queue/enqueue - Dashboard polls
media_jobscollection for job status - On completion: shows scraped media count + updates
creator_profiles.last_scraped_at
Security Best Practices
Key Rotation
To rotate the encryption key:-
Generate New Key:
-
Re-encrypt All Credentials:
-
Update
.envon Server -
Restart Services:
Backup & Recovery
Backup Encryption Key:- Store
CREDENTIALS_ENC_KEY_B64in password manager - Add to encrypted
.env.backupfile - Store in separate location from server
- All encrypted credentials are permanently unrecoverable
- Users must re-connect all platforms
- No decryption method exists without original key
Access Control
Environment Permissions:creator_profiles.credentials: Never expose via API to frontend- Dashboard should only display connection status, never raw credentials
- Admin role can view encrypted envelope (but not decrypt without key)
- Encryption/decryption always happens server-side
- Browser extension POSTs cookies to server (server encrypts)
- Dashboard never receives decrypted credentials
Troubleshooting
”Invalid Credential Envelope” Error
Cause: Encryption key mismatch or corrupted data. Fix:- Verify
CREDENTIALS_ENC_KEY_B64matches key used during encryption - Check envelope format starts with
v1: - Test decryption manually:
Scrape Job Stuck in “Running”
Cause: Stagehand session crashed or hung. Fix:- Check Stagehand logs:
pm2 logs stagehand-server - Restart Stagehand:
pm2 restart stagehand-server - Manually update job status:
“No Cookies Found” During Scrape
Cause:platform_sessions record missing or expired.
Fix:
- Check
platform_sessionscollection for matchingcreator_profile_id+platform - If missing: create HITL session (dashboard does this automatically)
- If expired: re-capture cookies via browser extension
- Verify cookie
expires_atis in the future
Platform Login Fails with Cookies
Cause: Cookies expired, platform session invalidated, or IP change. Fix:- Log into platform manually in browser
- Re-capture cookies via extension
- Trigger scrape again
- If still fails: platform may have changed auth mechanism (check for OAuth migration)
Related Documentation
- Browser Extension - Cookie capture workflow
- Stagehand Automation - Cookie injection and scraping
- Directus CMS -
creator_profilesandplatform_sessionscollections
