FIP: Live Activity Status #268
Replies: 7 comments 3 replies
|
Excellent proposal — the protocol-level approach to live presence is exactly right. A few thoughts on the design and some edge cases to consider: Strong Points1. Minimal protocol surface area: 2. Separation of concerns: 3. Cooperative write convention: Edge Cases & Considerations1. Heartbeat Failure During Long SessionsIf a user is in a 3-hour audio space and their client loses network connectivity for 10 seconds (exceeds freshness threshold), their presence disappears from all clients even though they're still in the space. When they reconnect:
Suggestion: Consider a client-side grace period where readers don't immediately hide presence when freshness lapses — e.g., show a "connection unstable" indicator for 10-15 seconds before fully removing presence. This is an off-protocol convention but worth documenting. 2. Rate Limit Budget ContentionThe 5000 writes/hour budget supports 1-second heartbeats (~3600/hour) with headroom. But if a user is simultaneously:
They could hit the budget ceiling. The cooperative write convention mitigates this, but only if all clients follow the Suggestion: The rate limit budget should be per-storage-unit (as proposed) but also document that clients SHOULD implement exponential backoff if they receive rate-limit rejections, rather than retrying at a fixed 1-second cadence. 3. URL Validation: HTTP vs HTTPS Redirect ChainsThe spec requires
Current validation: Protocol only validates parseability and Risk: A client resolving the URL for activity metadata could hit an insecure redirect chain, potentially leaking the user's request to an HTTP intermediary. Suggestion: Document that clients SHOULD refuse to follow redirect chains that downgrade from 4. Empty String Semantics & Zombie PresenceThe spec states
The freshness check (5 seconds) prevents zombie presence from being rendered, but the data persists on-chain. Mitigation already in place: Heartbeat requirement + freshness check means stale presence naturally becomes invisible. 5. Multi-Device Scenario: User in Space on Desktop, Browsing on MobileUser is:
Both devices have authorized signers. If mobile client is presence-aware and wants to report "online but not in activity," it would write a presence-only URL. But per the cooperative convention, presence writers SHOULD NOT overwrite a fresh activity URL. Result: Mobile client suppresses its heartbeat, desktop's activity signal is preserved. ✅ This works correctly. Edge case: What if the desktop app crashes (stops heartbeating) while user is still browsing on mobile? After 5 seconds, the activity signal goes stale. Mobile client could then write its presence-only URL — but the user might not be aware the desktop signal died. Suggestion: Clients could expose a "currently live on another device" indicator when they detect a fresh Implementation NotesStorage Pruning & Historical QueriesThe proposal states Question: Will hubs retain historical Suggestion: Document that historical live activity tracking is out of scope for the protocol layer — apps/indexers that want "user was live 5 times this week" should cache this data off-chain. Alignment with Companion FIP #269The URL + manifest pattern is the right design, but the two FIPs have a tight dependency:
Deployment sequencing:
Suggestion: Coordinate a simultaneous activation (same Snapchain release) or at minimum, publish both FIPs together so client implementers can build against the full stack. tl;dr: This is a minimal, well-scoped FIP that reuses existing protocol machinery elegantly. The edge cases above are mostly client-side concerns (grace periods, rate limit backoff) that don't require protocol changes — just documentation in implementation guides. Strong support for shipping this. |
FIP: Agent Access Payment Protocol (AAPP)Author: presdency.eth (FID 1079922) AbstractThis proposal introduces a standard for autonomous AI agent access to gated web resources via microtransactions on Base. An agent encounters a paywall, pays a fixed microfee (e.g. $0.01 USDC) to a site-controlled wallet, receives a signed access token, and proceeds — with no human input required. This turns the open web into a permissionless economy where sites earn passively and agents operate freely. The protocol delta is small:
MotivationAgents can't access the web autonomouslyAI agents are increasingly operating on behalf of humans — reading content, pulling data, executing tasks. But the moment they hit a paywalled or gated resource, they stop. They have no native mechanism to pay and proceed. Current workarounds:
There is no primitive today for: "this agent needs access, it will pay for it now, autonomously." Sites have no passive income layer for agent trafficAgent traffic is already a significant and growing share of web requests. Sites currently earn nothing from it — agents don't click ads, don't subscribe, don't convert. They just consume bandwidth and return nothing. AAPP gives every website owner a revenue stream from agent traffic with zero integration overhead. The right shape for thisThe solution is a single bounded interaction — a payment manifest that sites publish, and a payment flow that agents execute. Small enough to be cheap. Fast enough to be invisible. Open enough to be permissionless. Why Not Existing Primitives
SpecificationSite Manifest:
|
| Actor | Action | Outcome |
|---|---|---|
| Site owner | Publishes manifest | Earns USDC per agent visit |
| Agent operator | Pays per access | Gets content without human auth |
| Facilitator | Verifies payments | Earns small % fee |
| End user | None required | Agent completes task autonomously |
At 1 million agent calls per day across the ecosystem at $0.01 each:
- $10,000/day flows to site owners
- Facilitator at 1% = $100/day in protocol revenue
Security Considerations
- Replay attacks: Access tokens are bound to a specific
txhash. Reusing a token requires reusing the transaction — impossible. - Token theft: Tokens are FID-bound. A stolen token from FID A cannot be used by FID B without a valid signature.
- Overpayment: Manifests specify exact amounts. Agents should reject manifests requesting unusually high amounts before paying.
- Fake manifests: Agents should verify
manifest_signed_byagainst the site's onchain identity before paying.
Open Questions
- Should access tokens be revocable mid-duration if site detects abuse?
- Should the protocol support streaming payments (pay-per-request within a session) or only session-level access?
- Should FID be required or optional for agents not operating within Farcaster?
- Who audits facilitators? Is a facilitator registry needed?
References
- x402 Protocol — Coinbase
- EIP-3009: Transfer With Authorization
- FIP #268: Live Activity Status
- Praxis Protocol — Base
Copyright
This FIP is released into the public domain under CC0.
|
How about USER_DATA_TYPE_CURRENTLY instead of LIVE_AT Covers cases like 'is away', 'is watching', 'is crying over spilled milk', 'is flying to farcon' |
|
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. |
|
Two updates to the FIP, noting here so it's clear when they were made
|
|
Snapchain implementation: farcasterxyz/snapchain#899 |
|
Updated the FIP text to match the implementation that has now landed:
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Abstract
This proposal introduces a new
UserDataType—USER_DATA_TYPE_LIVE_AT— that lets a user signal they are currently participating in a live activity (an audio space, a livestream, a broadcast event) by writing a single canonical URL to their identity. The slot is last-write-wins: an authorized signer writes the activity’s URL on join and clears it on leave. Clients read the slot to render presence wherever a user surfaces — profile, feed, avatar dot, in-feed strips, channel pages — without indexing per-provider.The protocol delta is small:
UserDataTypeenum.Motivation
App-agnostic live presence
Live audio rooms, video streams, and event broadcasts are an increasing share of activity on social networks, but there is no Farcaster protocol primitive for “this user is in a live experience right now.” Clients that support this today today (e.g. Juke) cannot surface presence in other Farcaster clients.
The right shape for this signal is a single bounded slot on the user’s identity — written by the app the user has authorized, read by every client without coordination, and small enough to be cheap to query alongside the rest of the user’s profile.
Why not existing primitives
embeds: [{ url: spaceUrl }]. Posting a “live now” cast and removing it on end works, but it is ephemeral on the feed and mixes a property of the user with property of the cast. Clients have to read user’s casts to know whether the user is live.LinkBody.target_fidis FID-only; the protocol has no URL-targeted link.UserDataalready models “facts about the user that are true now” — pfp, bio, display name, location, twitter, github. “Currently live at” is a clean addition to that category and reuses every piece of machinery already in place: storage, signing, delegated keys, consensus, indexing, prune rules, and the public HTTP/gRPC read API.Specification
New
UserDataTypevalueThe wire format of
UserDataAddBody { type, value }is unchanged. A user writes the slot by submitting a signedMESSAGE_TYPE_USER_DATA_ADDwithtype = USER_DATA_TYPE_LIVE_ATandvalueset to the canonical URL of the live activity. A user clears the slot by writing the same message withvalue = "".LIVE_ATis activated by the Snapchain protocol feature gateLiveAtin engine versionV17. Validators running a version beforeV17rejectUSER_DATA_TYPE_LIVE_ATas unsupported.Value format
The
valuefield is a UTF-8 string subject to the following rules:"") — semantic meaning “not currently live.” Clients must treat the empty string as equivalent to the field being unset.https://URLs. Hub/client validation helpers enforce URL parseability and thehttps:scheme; Snapchain consensus validation enforces the final protocol feature gate and the UserData byte-size bound.Clients determine whether the URL refers to a live activity by resolving the URL — specifically by checking for Farcaster-specific meta tags (
fc:live=1) and an optional signed manifest endpoint (see the companion FIP: Live Activity Manifest). Examples:https://broadcast.app/space/abc123(audio session URL of a live activity),https://app.example/active(generic presence URL).Last-write-wins semantics
Per existing
UserDatarules, only the winningUSER_DATA_ADDfor a given(fid, type)pair is retained. Ordering is last-write-wins by Farcaster message ordering: highest timestamp wins, and same-timestamp ties are broken by the lexicographically higher message hash. Clearing the slot is a write ofvalue = ""that wins this ordering over the previous write. There is noUSER_DATA_REMOVE;value = ""is the canonical “not live” state.Liveness and freshness (off-protocol convention)
Snapchain has no native message-level TTL, and this proposal does not add one. The slot persists at its last-written value until overwritten or pruned. To approximate ephemeral semantics, this FIP specifies two off-protocol conventions:
valueat a regular interval — RECOMMENDED every 1 second, MIN 1 second, MAX 5 seconds — for as long as the user is in the activity. Each rewrite advances the message timestamp on the slot.LIVE_ATvalue as stale and not render it as “currently live” if the message timestamp is older than a freshness threshold — RECOMMENDED 10 seconds.Clients that want to display “was live 3 hours ago” can ignore the freshness threshold and render historical data — the message timestamp is retained regardless.
Cooperative writing convention
Multiple apps may have permission to write
LIVE_ATon the user’s behalf simultaneously (e.g., the user has Farcaster and Juke both authorized). To avoid wasted writes and lost activity URLs, apps writingLIVE_ATSHOULD follow this convention:LIVE_ATvalue and message timestamp.fc:live=1meta tags per the companion FIP) MAY still overwrite a presence-only URL, since activity is the more informative signal. Activity writes preempt presence writes.The net effect: one writer “owns” the slot at a time, activity writes preempt presence writes, and presence writers cooperatively yield when any writer is keeping the slot fresh. This also materially reduces
LIVE_ATrate limit consumption — only one app heartbeats at a time, rather than every authorized app writing in parallel.Risk: misbehaving clients. This convention is a
SHOULD, not aMUST— the protocol cannot enforce it. A client that ignores the convention and heartbeats unconditionally will clobber activity URLs the user is trying to surface (for example, a presence-only app overwriting an audio space URL, making the space disappear fromLIVE_AT-driven render surfaces despite the user still being in it). The mitigation is social: users will notice when a live activity they are participating in disappears from feeds and avatar pills, identify the misbehaving client as the cause, and either ask that client to fix it or stop using it. Apps that consistently violate the convention pay an organic UX cost.Single-valued
The slot holds exactly one URL. A user simultaneously present in two live activities writes the URL of whichever they want to surface; the URL’s manifest can declare cross-posting if relevant. The protocol does not model multi-activity presence. This also models human behavior where humans are unlikely to be live in two places at the same time. If agent behavior is different in the future, this can be modified as needed.
Validation flow
A
MESSAGE_TYPE_USER_DATA_ADDwithtype = USER_DATA_TYPE_LIVE_ATis validated identically to other UserData writes, with the following additional semantics:LiveAtprotocol feature is active (EngineVersion::V17or later). Reject as unsupported before activation.len(value) <= 256bytes and thatvalueis valid UTF-8 as required for UserData strings.value == "", accept as the canonical clear/not-live value (subject to existing UserData rules).value != "", hub/client validation helpers MUST require a parseable URL with schemehttps:.No fetching, no URL scheme allowlist beyond
https:, and no manifest validation is performed as part of message validation.Storage and pruning
The slot occupies one record in the per-FID UserData store. The default storage unit allots approximately 800 UserData records across all
UserDataTypevalues combined; under last-write-wins semantics, repeated writes toLIVE_AToverwrite in place rather than accumulating, so steady-state cost is one record per activeUserDataTyperegardless of write frequency.This proposal does not change UserData storage limits.
Rate limit
USER_DATA_TYPE_LIVE_ATwrites have a separate rate limit budget from the existing per-FID general message budget.LIVE_ATwrites do not count against the general budget, and other writes do not count against theLIVE_ATbudget. This separate budget is sized to support the recommended 1-second heartbeat without consuming the user’s general tx budget.The implemented budget is 5000 writes/hour per storage unit for
LIVE_AT, using the user's current storage-unit count. A user with zero storage units has zeroLIVE_ATbudget and theirLIVE_ATwrites are rejected by the mempool rate limiter. Storage changes invalidate the cachedLIVE_ATlimiter for that FID so quota reflects the updated storage-unit count.This is the one protocol-level rate-limit change required by this FIP. Without it, a single live user heartbeating every second would exhaust their entire general tx budget within ~8 minutes of being live, blocking casts and other writes for the rest of the hour.
Client interpretation
Clients reading
LIVE_ATinterpret it at two fidelities:valuewhose message timestamp is within the freshness window means the user is currently active on Farcaster. No URL fetch is required for this render. The semantic is “this user is reachable right now,” and it is true regardless of which app or activity is writing the slot. Surfaces like DM lists, contact rosters, and profile pills typically need only this.fc:live=1,fc:live:hostFid, etc.) carrying dynamic state. The host's identity is bound to the URL by a JFS signature served at a manifest endpoint linked from the URL; trust-sensitive surfaces verify it. Chat is the reply tree of a cast referenced by the URL'sfc:castmeta tag.Clients SHOULD combine the lifecycle signals from the URL's meta tags (e.g.,
fc:live:endedAt) with the message-level freshness rule above and treat the slot as stale if either signal indicates the activity has ended.The URL written to
LIVE_ATdoes not have to be a live activity URL. An app reporting a user as merely active can write any URL it controls; clients will still light the green dot from freshness alone. The richer activity render only kicks in when the URL serves the meta tags + manifest contract.The protocol FIP and the activity FIP are independent.
LIVE_ATcan ship before the activity contract is finalized; the activity contract can ship later or in parallel without revisiting this protocol-level change.Design rationale: an opaque URL, not a structured payload
The slot value is intentionally a single URL with no companion fields — no
LIVE_KINDto declare activity type (audio/video/event), noLIVE_TITLE, noLIVE_HOST_FID, no listener count. Everything beyond “where” is delegated to the resource the URL points to (an OG-style manifest, a Frame, or any HTTP response the client can dereference). This is a load-bearing design choice and an explicit non-goal to expand:LIVE_KIND, say — must be kept in sync withLIVE_ATunder independent last-write-wins semantics perUserDataType. Interleaved or partial writes produce inconsistent states (a user withLIVE_AT = audio-room-urlandLIVE_KIND = video) that the protocol has no way to reconcile.kind) into a protocol slot saves zero round-trips while doubling protocol surface area.audio/video/eventinto a protocol enum locks the network into today’s view of “kinds of live experiences.” Listening parties, live coding, AR sessions, classroom sessions, gameshows, watch-alongs — each new category would require a FIP and a validator upgrade. The URL’s manifest evolves at app speed without protocol coordination.If a future use case genuinely cannot be served by the URL+manifest pattern, that is a strong signal to reopen this design at that time.
Discoverability (informative)
Live activities are identified by the audio session URL — see the companion FIP: Live Activity Manifest for the meta tags, signed manifest, and cast conventions that make it renderable.
LIVE_ATpoints at this audio URL; speakers and listeners share the same value.Chat is the reply tree of a cast (created by the host's client when the activity starts) that references the audio URL as an embed. The audio URL's
fc:castmeta tag points back at this cast. Space chat behaves like normal replies, naturally placed in whatever social context the cast lives in (channel, thread, top-level feed).Implementation in Snapchain
Key changes
USER_DATA_TYPE_LIVE_AT = 10to theUserDataTypeenum in the protobuf schema.UserDatavalidation path: whentype == USER_DATA_TYPE_LIVE_ATandvalue != "", parsevalueas a URI and reject on failure.(type, fid)as today; no new index required.pfp,bio, etc.MESSAGE_TYPE_USER_DATA_ADDwrites withtype == USER_DATA_TYPE_LIVE_AT(RECOMMENDED 5000/hour per storage unit), independent of the general per-FID rate limit.What doesn’t change
MESSAGE_TYPE_USER_DATA_ADDcan writeLIVE_AT. Granular per-UserDataTypepermissions are out of scope here and can be found in this [FIP separately](FIP: Snapchain Signers #266).Performance
The change adds one enum value, one validation branch, and a separate rate limit budget for
LIVE_ATwrites. Validation cost is negligible; the meaningful cost is the throughput supported by the new rate limit. With 1-second heartbeats, a single active live user produces ~3600 writes/hour, so validator capacity scales with the number of concurrent live users on the network. Pruning behavior is unchanged — under last-write-wins, repeatedLIVE_ATwrites overwrite in place rather than accumulating storage.Privacy Considerations
Disclosure is opt-in
Writing
LIVE_ATis a deliberate act by the user — directly or via an app key the user has authorized. Apps SHOULD NOT writeLIVE_ATfor token-gated, private, or otherwise non-public activities unless the user has explicitly opted in.Backwards Compatibility
This is an additive change. Clients unaware of
USER_DATA_TYPE_LIVE_AT = 14receive the record from existing UserData APIs and either ignore it or render the raw value as a string of unknown type. No existing message becomes invalid; no existing field changes shape.Adoption requires a coordinated Snapchain release that recognizes the new
UserDataTypevalue in the validation path. Validator nodes running an older release, or running before theLiveAtprotocol feature is active in engine versionV17, rejectUSER_DATA_ADDmessages carryingUSER_DATA_TYPE_LIVE_ATas invalid/unsupported. Once a release containing this change is adopted across the validator set and the feature is active, blocks proposed under the new rules are accepted by the upgraded quorum and the new type flows through the existing UserData read APIs. The upgrade path is the same as for any prior protobuf enum addition (e.g.,LOCATION,TWITTER,GITHUB).References
UserDataTypeenumMESSAGE_TYPE_USER_DATA_ADDspecAll reactions