FIP: Live Activity Manifest #269
Replies: 4 comments 3 replies
|
This proposal is a good idea, but carries a few issues that reduces the practicality of its intent:
I'll break each down. using meta tags to convey session statemeta tags are frequently cached for long durations, as their intention is to be a long-lived signal to applications towards the content itself. If a session is over, the data in its final state is fine, but prior to that (including the existing ingestion pipeline for your backends), this data is cached for a long time, meaning useful status signals like participant count and speaker fids will likely be wrong for the effective duration, most likely the original data set in the first place when casted. This should be surfaced through a different method, e.g. any http method that helps cache bust against aggressive CDNs a lot of service providers wrap around frontends, i.e. POST/PATCH/PUT no content negotiation/multiplexing considerationopen audio/video streams often follow one of two approaches: WebRTC or RTMP, sometimes together. it would be helpful to align the metadata to the necessary information to connect for this, otherwise you're shifting the burden to the website delivering the stream, to which point, it basically carries no meaningful difference from a miniapp and this FIP itself could be skipped altogether, opting to point to a miniapp as-is for the companion FIP. surfacing this information at the metadata level lets a user know:
which is crucial for actually integrating support for this, otherwise it ends up being informal standards that clients will impose, and have to adapt to whenever a popular client decides to set the "standard" additionally, by not taking on this burden, there is a huge security issue, in that most webrtc implementations do not care in the slightest about revealing user metadata, like IP addresses. as an example, a mini app could set up a webrtc session, and intentionally or not, create the opportunity for a user of the miniapp to collect a mapping of IPs to FIDs, regardless of whether the miniapp intended this. if a client were to embed this directly, it increases the likelihood that this attack could work. no standard path to interpretabilityas mentioned in the previous point, meaningfully integrating this has a wide-open gap for informal "standards" to emerge. if clients are to support this, they inevitably would, if the FIP is taken as-is, do whatever the most popular client does, and when the primary client changes that interpretation, it would break the other clients. |
|
@CassOnMars raises valid concerns — the meta tag caching issue and content negotiation gap are real problems. But I think the proposal's core design (opaque URL + manifest pattern) is salvageable with a few targeted fixes. Addressing the Critiques1. Meta Tag Caching / Stale Participant CountsProblem: CDNs cache meta tags; dynamic state (speaker list, participant count) goes stale. Solution: HTTP cache headers + versioned manifest The spec should REQUIRE that URLs serving For clients that want aggressive caching (mobile data savings), the manifest could include a {
"hostAttestation": { ... },
"cacheHint": {
"maxAge": 5, // seconds
"staleWhileRevalidate": true
}
}This makes caching opt-in and provider-controlled, not CDN-imposed. Alternative: Move dynamic state entirely to the manifest endpoint (not meta tags). Meta tags become static descriptors:
Clients MUST fetch the manifest for real-time data. This separates "what kind of thing is this" (cacheable) from "what's happening right now" (uncacheable). 2. Content Negotiation / Protocol Selection (WebRTC vs RTMP)Problem: Clients don't know how to connect (WebRTC? RTMP? HLS?) without fetching the page. Proposal: Add <meta property="fc:live:protocol" content="webrtc+hls" />Values: Clients can decide before embedding:
This also solves the IP leakage issue: clients that don't support WebRTC (or choose not to for privacy) can refuse to render WebRTC-only activities. Manifest extension: {
"protocols": [
{
"type": "webrtc",
"sdp": "https://signal.example/offer",
"privacy": "exposes-ip" // warning flag
},
{
"type": "hls",
"playlist": "https://cdn.example/stream.m3u8"
}
]
}3. Security: IP Address Leakage via WebRTCCass is right — this is a real attack vector. Mini-app embeds a "live activity" that's actually a WebRTC signaling page; user's IP leaks to the provider. Mitigation:
This should be explicit in the FIP — a "Security Considerations" section documenting the WebRTC IP leakage risk and recommended client mitigations. 4. No Standard Path to InterpretabilityProblem: Without content negotiation, clients can't know what they're getting until they fetch. Proposal: Adopt a typed manifest schema The manifest (fetched from {
"type": "live-audio-space", // or: live-video, live-event, live-stream
"version": "2026.1",
"hostAttestation": { ... },
"metadata": {
"title": "...",
"description": "...",
"image": "..."
},
"state": {
"startedAt": 1736054400,
"participantCount": 42,
"speakerFids": [...]
},
"protocols": [...]
}
This also future-proofs the spec: new activity types ( On "Informal Standards" RiskCass warns that without negotiation/typing, clients will "impose standards" and fragment. This is valid — but the FIP does prevent the worst case by:
The gap is that the manifest schema is currently too loose ("any JSON that looks like this example"). Tightening it to a versioned, typed schema would address the interpretability concern. Recommended Changes to FIP #269
tl;dr: Cass's critiques are sharp, but fixable. The URL+manifest pattern is sound — it just needs tighter contracts (caching rules, protocol negotiation, security disclosures) and a formal schema to prevent fragmentation. With those additions, this becomes a robust extension point that scales to future activity types without protocol churn. Strong support for the core design; recommend revisions addressing the above before merging. |
|
Hey! I'm a Base L2 game developer working on Base Invaders (Phaser 3 + Base mainnet) and wanted to share a real-world use case for live activity from a game dev perspective. With the new Farcaster Snaps, I built an example showing how on-chain game devs can: Embed a "Play Now" button directly inside a Farcaster cast via a Snap endpoint Pass Farcaster FID to Base MiniKit on Snap load Trigger a Base mainnet transaction (start game session, mint score NFT) from the cast Display results back inside the cast Full working example (PR): builders-garden/base-minikit-starter#1 How this connects to FIP #268 (live activity): A game session Snap that updates in real-time as players submit scores on-chain Cross-client support (Warpcast, Juke, etc.) for game tournaments happening on Base Would love feedback on whether the current proposal covers on-chain game session use cases, or if there are specific fields to consider for transaction-driven live updates. |
|
I think I would prefer to see an extension to casts, instead of user data. The ideal way would be a really extensible system for casts, something we have discussed so many times, and each time it looses in favor of a quick and dirty addition here and there (very long casts, location, banners, and so on) But if we don't want to do the real thing, an alternative is adding TTL to a cast. The TTL will be a soft indicator of validity (i.e. it's up to the clients to use it, it will not be removed from the snapchain). A client may decide to use this cast as a presence/live activity indicator (show it at the top of the profile? add a border or a dot to the pfp?) if the TTL is still valid (or the last one casted if more than one are valid). But it can also be valuable in casts like "24h more to mint", or "registrations to the event are closing in 3 days". |
Uh oh!
There was an error while loading. Please reload this page.
Abstract
A live activity (audio space, livestream, broadcast event, listening party) is identified in Farcaster by an
https://URL — the audio session URL, served by a third-party provider. The host cryptographically attests to that URL with a JFS signature served at a manifest endpoint linked from the URL; dynamic state (host FID, speakers, participant count, lifecycle, recording) is served as Farcaster-specific OpenGraph meta tags on the same URL.A cast is also created (by the host's client when the activity starts) referencing the audio URL as an embed. That cast is the chat anchor — chat in the activity is the standard reply tree of the cast. The audio URL points back at the cast via an
fc:castmeta tag.Discoverability via the
USER_DATA_TYPE_LIVE_ATslot defined in the companion FIP: Live Activity Status points at the audio URL.No protocol-level (Snapchain) change is required.
Motivation
USER_DATA_TYPE_LIVE_ATcarries an opaque URL. To render that signal as a live activity card, every Farcaster client must resolve the URL and extract metadata. Without a standardized contract, providers expose proprietary APIs (forcing per-provider client integrations) or fall back to OpenGraph (which lacks live-activity-specific fields).This FIP standardizes:
The cast convention makes space chat behave like normal replies, integrates spaces into existing conversation surfaces (channels, threads), and requires no special-case treatment of "chat under spaces."
Specification
Audio URL
The audio session URL is the canonical identifier for a live activity. Typically a third-party provider URL, e.g.
https://broadcast.app/space/abc123.USER_DATA_TYPE_LIVE_ATpoints at this URL when a user is in the activity — for the host and for every participant (speakers and listeners share the same URL value).Activity meta tags
The audio URL SHOULD serve Farcaster-specific OpenGraph-style meta tags carrying dynamic state. The presence of
fc:live=1is what identifies a URL as a live activity:Required:
fc:live,fc:live:hostFid, plus the manifest pointer link. All other tags are optional. Multi-valued tags (speakerFids) are comma-separated integers with no spaces.fc:live:startedAt,fc:live:expectedDurationSec, andfc:live:endedAtare all optional. Activities with no known end time (e.g., an impromptu huddle) simply omitendedAtandexpectedDurationSec; lifecycle is then driven byLIVE_ATfreshness on the host's slot, plus an explicitendedAtset by the provider when the activity actually ends. OnceendedAtis present, it is permanent — providers SHOULD NOT revert an ended page back to live.fc:cast(when present) carries the hash of the cast that anchors chat for this activity (see Cast and chat below). Speakers and listeners are surfaced infc:live:speakerFids; the broader participant list is intentionally not enumerated in meta tags (useLIVE_ATcross-checks if needed).A non-Farcaster client (or one without live activity support) sees standard OpenGraph tags and renders the URL as a regular unfurl. This fallback is the desired behavior.
Manifest (host attestation)
The manifest endpoint linked from the audio URL serves a signed envelope binding the URL to the host's Farcaster identity. The signature is structurally identical to the
accountAssociationJFS used by Mini Apps — sameheader/payload/signatureshape, base64url JSON, Ed25519 over${header}.${payload}— with the payload claiming the audio URL instead of a domain.The manifest contains only the host attestation. All other information lives in meta tags on the audio URL itself. This keeps the manifest small, cacheable, and lets clients verify host identity without parsing dynamic state.
A client verifying the manifest:
hostAttestation.headerandhostAttestation.payload.headeras JSON. Verifyheader.type === "auth"andheader.fidmatches thefc:live:hostFidmeta tag served at the audio URL.payloadas JSON. Verifypayload.urlmatches the audio URL the client is resolving.${hostAttestation.header}.${hostAttestation.payload}and verify the Ed25519 signature usingheader.key.header.fid. Verifyheader.keyis in that set.Skipping verification and trusting the meta tags directly is not recommended but is possible if the clients wish to do so, accepting the risk that an audio URL could lie about its host without the JFS check.
The host signs the URL claim once, when the activity is initiated — typically the host's Farcaster client constructs the JFS using the user's active app key (the same delegated key used for casts) and hands it to the audio provider, which serves it back at the manifest endpoint. Subsequent state changes (participant count, ended state, recording URL) update meta tags only — no re-signing.
This signature is what prevents impersonation: if someone copies the audio embed URL into their own cast to share or amplify the activity, they cannot make themselves appear as the host. The host claim is bound to the URL via JFS, not to whoever embedded the URL.
Cast and chat
A cast is created by the host's client when the activity starts. The cast has:
embeds[0].url— the audio URL.parent_cast_hash(optional) — for activities in a thread or conversation context.parent_url(optional) — for activities scoped to a Farcaster channel.Cast text is unstructured user content — the host writes whatever they want. Title and description for the activity itself live in meta tags on the audio URL, not in the cast text.
Chat in the activity is the standard reply tree of this cast — replies use
parent_cast_idset to the cast's hash. There is no separateparent_url-anchored chat tree. Replies inherit cast-level moderation, channel context, and reply-tree behaviors (threading, mentions, embeds in chat messages). Replies are not top-level content — they appear in the reply surface, matching how users think about chat.The audio URL's
fc:cast=<hash>meta tag points at this cast, so clients resolving the audio URL can find the chat anchor without scanning Snapchain for casts that embed the URL.Resolution flow
A client resolving an activity URL
<url>(typically discovered viaLIVE_AT):<url>with standard browser-like Accept headers.fc:liveis absent or its content is not1, the URL is not a live activity — render as a plain unfurl (standard OpenGraph) or as the URL itself.fc:live,fc:live:hostFid) and the manifest pointer. On any required tag missing or malformed, falls back to a plain OpenGraph unfurl.hostAttestationper the Manifest section. On verification failure, the client MUST un-render or annotate the card to reflect that host identity could not be verified.fc:castmeta tag (if present) to render the reply tree as chat.fc:live:endedAt/fc:live:expectedDurationSeclifecycle signals (when present) with theLIVE_ATmessage-level freshness rule (see the companion FIP) to decide whether to render the activity as currently live.Discovery via
LIVE_ATSee the companion FIP: Live Activity Status (
USER_DATA_TYPE_LIVE_AT) for how presence and discovery work. In short:LIVE_ATpoints at this audio URL; the host's freshness drives "is this still live."Privacy considerations
Recording URLs
fc:live:recordingUrlis a public link. Providers gating recordings behind paywalls or token gates SHOULD pointrecordingUrlat a page that handles access checks rather than serving raw audio/video.References
accountAssociationJFS precedent.All reactions