Skip to content

Instantly share code, notes, and snippets.

@jeongseup
Last active January 7, 2026 07:40
Show Gist options
  • Select an option

  • Save jeongseup/5f752842e566decdc8a56116caf344bd to your computer and use it in GitHub Desktop.

Select an option

Save jeongseup/5f752842e566decdc8a56116caf344bd to your computer and use it in GitHub Desktop.
spec

Social Persona Card System – Specification

Overview

This system extends the existing miniapp (BaseCord-style card) by:

  • Continuously collecting Farcaster activity for registered users (by fid).
  • Analyzing that activity into a structured social persona and metrics for self-promotion / simple CV.
  • Generating visual cards (like the existing design) driven by this structured data.

The system is backend-first: the frontend miniapp consumes a stable API and “card context” JSON to render cards.


Goals & Non‑Goals

Goals

  • Automatically ingest and store Farcaster casts and profile stats per user.
  • Derive meaningful social metrics (activity, engagement, graph) without LLM.
  • Use an LLM to generate:
    • Topics and persona labels.
    • Short promotional summaries and taglines.
  • Provide an API to:
    • Trigger card generation.
    • Fetch latest persona + metrics + card context.
  • Support instant card regeneration without waiting for live sync (data is maintained in background).

Non‑Goals (v1)

  • No posting or replying on Farcaster on behalf of users.
  • No editing of cards directly inside the image layer (edits are done via persona/card context data).
  • No cross-platform aggregation (only Farcaster in this spec).

Domain Concepts

  • User
    A person with a Farcaster identity known by fid and attached to an account in the miniapp.

  • Cast
    A Farcaster post (text, embeds, interactions).

  • Social Metrics
    Aggregated numeric data derived from casts and profile (e.g., average casts/week, followers count).

  • Social Persona
    LLM-derived description of the user’s tone, topics, and self-promotion copy.

  • Card Context
    The final structured payload used by the design system to render a card.


Data Model

1. users

Represents a known Farcaster user.

  • id (UUID, PK)
  • fid (bigint, unique, indexed)
  • username (string)
  • display_name (string)
  • avatar_url (string)
  • created_at (timestamp)
  • updated_at (timestamp)
  • last_synced_at_casts (timestamp, nullable)
  • last_synced_at_persona (timestamp, nullable)

2. casts

Stores individual Farcaster casts.

  • id (UUID, PK)
  • fid (bigint, FK → users.fid, indexed)
  • cast_hash (string, unique)
  • timestamp (timestamp, indexed)
  • text (text)
  • channel (string, nullable)
  • client (string, nullable)
  • mentions (jsonb: list of mentioned fids or usernames)
  • replies_count (int)
  • recasts_count (int)
  • likes_count (int)
  • raw (jsonb: full provider payload)

Soft limit: only keep the last N casts per user (e.g., 2–3k) if storage becomes a concern.


3. social_metrics

Snapshot of numeric metrics per user.

  • id (UUID, PK)
  • fid (bigint, FK → users.fid, indexed)
  • snapshot_at (timestamp, indexed)

Derived metrics (examples):

  • Activity

    • casts_last_7d (int)
    • casts_last_30d (int)
    • avg_casts_per_week (numeric)
    • first_cast_at (timestamp)
    • last_cast_at (timestamp)
  • Engagement

    • avg_likes_per_cast (numeric)
    • avg_recasts_per_cast (numeric)
    • avg_replies_per_cast (numeric)
    • top_cast_like_count (int)
    • top_cast_hash (string, nullable)
  • Graph

    • followers_count (int)
    • following_count (int)
    • follow_ratio (numeric)

4. social_persona

LLM-derived interpretation of a user’s social presence.

  • id (UUID, PK)
  • fid (bigint, FK → users.fid, indexed)
  • generated_at (timestamp, indexed)

Content fields (stored as JSON or columns):

  • tone (string; e.g., "technical and helpful")
  • primary_topics (jsonb array of strings)
  • secondary_topics (jsonb array of strings)
  • persona_labels (jsonb array of strings; e.g., ["Developer", "Infra builder"])
  • summary (short paragraph; 1–3 sentences)
  • sample_quotes (jsonb array of short strings)
  • raw_json (jsonb; exact LLM output for debugging)

Multiple rows per user allow persona history over time.


5. card_context

A materialized snapshot of everything needed to render one card.

  • id (UUID, PK)
  • fid (bigint, FK → users.fid, indexed)
  • persona_id (UUID, FK → social_persona.id)
  • metrics_snapshot_at (timestamp)
  • created_at (timestamp)

Rendered fields:

  • headline (string; main label on card, e.g., "Infra & DeFi Builder")
  • subheadline (string; e.g., "@basename • Developer")
  • top_topics (jsonb array of strings; shown as chips)
  • highlight_stats (jsonb; array like { label, value })
  • summary_line (string; single sentence if used in UI)
  • style_meta (jsonb; template name, color theme, variant)
  • raw_json (jsonb; full context used to render the card)

System Components

1. Ingestion Worker

Responsible for Step 1 in the original spec.

Responsibilities

  • Periodically fetch new casts and profile stats for each registered user.
  • Write/append scripts for:
    • casts
    • social_metrics
    • users basic profile updates.

Inputs

  • fid list from users table.
  • Service credentials for Farcaster data provider (e.g., Neynar).

Process

  1. For each user:
    • Fetch profile (followers/following, username, display_name, avatar).
    • Fetch casts since last_synced_at_casts.
  2. Upsert new casts into casts.
  3. Compute metrics and insert a new social_metrics row.
  4. Update users.last_synced_at_casts.

Outputs

  • Updated users, casts, social_metrics.

Scheduling

  • Every N minutes/hours (configurable).
  • Optional: separate cron for high-activity users.

2. Persona Worker (AI Agent Chain)

Responsible for Step 2 in the original spec.

Responsibilities

  • Periodically or on demand, compute social_persona from recent data.
  • Use an LLM to infer topics, tone, and summary.

Inputs

  • fid.
  • Recent casts (e.g., last 200–500).
  • Latest social_metrics snapshot.

LLM Interface

Input JSON (example):

{
  "metrics": {
    "avg_casts_per_week": 3.4,
    "avg_likes_per_cast": 12.1,
    "followers_count": 954,
    "following_count": 410
  },
  "sample_casts": [
    {"text": "example cast text 1", "likes": 24, "recasts": 3, "replies": 4},
    {"text": "example cast text 2", "likes": 3, "recasts": 0, "replies": 1}
  ]
}

Expected output JSON:

{
  "tone": "technical and helpful",
  "primary_topics": ["L2 infra", "DeFi", "Farcaster dev tooling"],
  "secondary_topics": ["personal updates", "memes"],
  "persona_labels": ["Developer", "Infra builder"],
  "summary": "Onchain infra-focused developer sharing hands-on tips and updates about DeFi and L2 scaling.",
  "sample_quotes": [
    "I like turning complex infra into simple tools for builders.",
    "Most people underestimate how much UX matters in onchain apps."
  ]
}

Process

  1. Load metrics + sample casts from DB.
  2. Call LLM with strict JSON schema.
  3. Validate output (types, required fields).
  4. Insert new social_persona row.
  5. Update users.last_synced_at_persona.

Triggers

  • Time-based (e.g., once per day per active user).
  • Or threshold-based (e.g., N new casts since last persona).

3. Card Context Generator

Responsible for Step 3 in the original spec.

Responsibilities

  • Assemble a card_context record on demand when user requests a card.
  • Optionally call LLM for a catchy headline or tagline, but can be deterministic.

Inputs

  • fid.
  • Latest social_persona and social_metrics for that user.

Process

  1. Load latest persona and metrics.
  2. Construct:
    • headline (derived from persona labels/topics).
    • subheadline (display_name + handle + role).
    • top_topics (top 3 primary topics).
    • highlight_stats (e.g., casts/week, avg likes, followers).
    • summary_line (short version of persona summary).
    • style_meta (template name, e.g., "basecard-v1").
  3. Insert into card_context.
  4. Return created card_context row to caller.

API endpoint (example)

  • POST /users/:fid/cards
    • Body: optional options (template variant, language).
    • Response: card_context JSON + card_context.id.

4. Design System & Renderer

Responsible for Steps 4 and part of 3 in the original spec.

Responsibilities

  • Render visual cards like the existing BaseCord-style image, but enriched with social info.
  • Support multiple design templates while consuming the same card_context structure.

Inputs

  • card_context JSON.

Rendering Options

  • Client-side React component (for miniapp) reading card_context.
  • Server-side renderer that outputs:
    • HTML/CSS (for share pages).
    • PNG/OG-image (for sharing on other platforms).

Layout Ideas

  • Left: avatar / mascot or user PFP.
  • Right top: headline, display_name, handle.
  • Right middle: role text; topic chips under it.
  • Right bottom: small metrics row:
    • “Casts/week”, “Avg likes”, “Followers”.
  • Branding: existing BaseCord logo and border style.

5. Sync & Freshness

Responsible for Step 5 in the original spec.

  • Background ingestion and persona workers keep data fresh.
  • On each user visit:
    • Frontend can call GET /users/:fid/card-latest which:
      • Returns latest card_context if recent enough.
      • Optionally enqueues a “refresh now” job in the background if stale.
  • Users can press “Regenerate card” to force a new card_context based on latest persona/metrics.

Freshness policy is configurable (e.g., consider persona “fresh” if < 24 hours old and < N new casts since generation).


APIs

1. Fetch latest card context

GET /users/:fid/card-latest

  • Response 200
{
  "card_context": {
    "id": "uuid",
    "headline": "Infra & DeFi Builder",
    "subheadline": "@basename • Developer",
    "top_topics": ["Farcaster dev", "DeFi", "L2 scaling"],
    "highlight_stats": [
      {"label": "Casts/week", "value": "3.4"},
      {"label": "Avg likes", "value": "12"},
      {"label": "Followers", "value": "954"}
    ],
    "summary_line": "Onchain infra-focused developer sharing DeFi and L2 scaling tips.",
    "style_meta": {"template": "basecard-v1"},
    "created_at": "2026-01-07T00:00:00Z"
  }
}

2. Trigger regeneration

POST /users/:fid/cards

  • Body (optional)
{
  "template": "basecard-v1",
  "language": "en"
}
  • Response 201
{
  "card_context_id": "uuid",
  "status": "ready"
}

3. Admin/maintenance endpoints (optional)

  • POST /admin/users/:fid/ingest-now – trigger immediate ingestion.
  • POST /admin/users/:fid/persona-now – trigger persona recomputation.

Implementation Notes

  • Keep LLM prompts small by sending compressed sample casts (e.g., top N by engagement plus random sample).
  • Strictly define JSON schemas and validate all LLM responses before writing to DB.
  • Treat card_context as immutable snapshots (good for debugging and letting users “freeze” a version they like).
  • When you later add other chains (X, GitHub, onchain data), you can extend social_metrics and social_persona without breaking the card API; card templates just see richer fields.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment