10 KiB
bLIP 10
Original Author: Satoshis Stream <satoshisstream@pm.me>
Created: 2022-01-24
License: CC0
Abstract
Using Value 4 Value apps, listeners can send sats to the hosts of the podcasts they listen to. Per minute sending is called stream and manual payments are called boosts. If there is a message to the podcaster, with a boost, it is called a boostagram.
These apps (see Reference Implementations) adopted bLIP 10 with the 7629169 TLV type for inputting key-value JSON metadata about the sent payment. The TLV holds data about the timestamp when the payment was sent within the episode, the name of the podcast and its Guid, among other fields described in the Specification section.
Example:
{
"podcast": "PODCASTNAME",
"feedID": 1337,
"episode": "EPISODENAME",
"action": "boost",
"ts": 33
}
Copyright
This bLIP is licensed under the CC0 license.
Specification
The sender of the payments (the podcast app) needs to input some metadata so the podcast host can identify whom the payment is for. Most fields are optional.
Formatting and encoding
A flat key-value json structure is used where the keys listed below can be set. The json string is then encoded into utf8 and attached to the keysend payment. Receivers of messages must be aware that there is no guarantee for the order of the keys.
Sample JSON string, containing a selection of keys:
{"app_name": "Castamatic", "app_version": "8.0.6", "value_msat_total": 49960, "url": "https://feeds.buzzsprout.com/1844352.rss", "podcast": "Mere Mortals", "action": "stream", "episode": "The Art Of NFT's & Aimless Wandering", "episode_guid": "Buzzsprout-9931017", "value_msat": 97940, "ts": 574, "name": "Podcaster", "sender_name": "Peter"}
Treated as utf8, the hex value of this json record would be:
7b226170705f6e616d65223a202243617374616d61746963222c20226170705f76657273696f6e223a2022382e302e36222c202276616c75655f6d7361745f746f74616c223a2034393936302c202275726c223a202268747470733a2f2f66656564732e62757a7a7370726f75742e636f6d2f313834343335322e727373222c2022706f6463617374223a20224d657265204d6f7274616c73222c2022616374696f6e223a202273747265616d222c2022657069736f6465223a202254686520417274204f66204e4654277320262041696d6c6573732057616e646572696e67222c2022657069736f64655f67756964223a202242757a7a7370726f75742d39393331303137222c202276616c75655f6d736174223a2039373934302c20227473223a203537342c20226e616d65223a2022506f64636173746572222c202273656e6465725f6e616d65223a20225065746572227d0a
If a field is indicated to be a str in the fields-list, that means it is a JSON string (within quotes) and ints are plain numbers.
Fields
Identifying the podcast required: use any of podcast, guid, feedID and/or url. guid preferred
podcast(str) Title of the podcastfeedID(int) ID of podcast in PodcastIndex.orgurl(str) RSS feed URL of podcastguid(str) The<podcast:guid>tag.
Identifying the episode recommended: use any of episode, itemID and/or episode_guid. episode_guid preferred
episode(str) Episode of the podcastitemID(int) ID of episode in PodcastIndex.orgepisode_guid(str) The GUID of the episode
Information about time within the episode recommended: use any of ts, time. ts preferred
ts(int) Timestamp when the boost/stream was sent WITHIN the episode, in seconds (playback position)time(str) HH:MM:SS timestamp when the boost/stream was sent WITHIN the episode (playback position)
Rest of keys:
actionrecommended: (str) "boost", "stream" or "auto"app_name: recommended (str) Name of sending appapp_version: (str) Version of sending appboost_link: (str) App specific URL containing route to podcast, episode, and/or timestamp at time of the actionmessage(str) Text message to add to the boost message: a boostagramname(str) Name for this split in value tagsender_namerecommended (str) Name of sender (free text, not checked by signatures)sender_id(str) Static random identifier for users, not displayed by apps to prevent abuse. Apps can set this per-feed or app-wide. A GUID-like random identifier or a hash works well. Max 32 ascii characters.speed(str) Speed in which the podcast was played, in decimal. So 0.5 is half speed and 2 is double speed.uuid(str) Unique UUID of the paymentvalue_msat_total: recommended (int) TOTAL Number of millisats for the payment (all splits together, before fees. The actual number someone entered in their player, for numerology purposes.)
Reference Implementations
- Breez: https://github.com/breez/breezmobile/blob/4cf057c066d03c155964f0c4db43476cd70a57ab/lib/bloc/podcast_payments/podcast_payments_bloc.dart
- Podverse: https://github.com/podverse/podverse-shared/blob/fff84c0143dea4fa01241109b8666d4c0b9a6ffc/src/satoshiStream.ts
- PodStation: https://github.com/podStation/podStation/pull/249
- Helipad: https://github.com/Podcastindex-org/helipad/blob/203e72dafb65e4f9e89540fbe4fc07a456a9907a/src/main.rs
- Rust-V4V: https://github.com/Conshax/Rust-V4V/blob/master/src/boostagram.rs
Examples & App Specifics
Podverse
Podverse cuts the episode name short and stream actions are sent as streaming.
Boost
{
"podcast": "Test Podcast Anchor",
"feedID": 6015671,
"episode": "this is a very very very very very very very very very very ",
"episode_guid": "12b4df54-af38-4c53-8099-82f9caacdcd5",
"ts": 24,
"action": "boost",
"speed": "1",
"pubkey": "podverse-pubkey",
"value_msat_total": 100000,
"uuid": "75758d19-c4af-4da2-80ce-a5c84a0f1642",
"app_name": "Podverse",
"name": "Alby Test User PUT",
"sender_name": "Alwin_Conshax",
"message": "test"
}
Stream
{
"podcast": "Test Podcast Anchor",
"feedID": 6015671,
"episode": "this is a very very very very very very very very very very ",
"episode_guid": "12b4df54-af38-4c53-8099-82f9caacdcd5",
"ts": 315,
"action": "streaming",
"speed": "1",
"pubkey": "podverse-pubkey",
"value_msat_total": 100000,
"uuid": "654072b5-9825-4792-b502-31ee9f1ec6aa",
"app_name": "Podverse",
"name": "Alby Test User PUT",
"sender_name": "Alwin_Conshax"
}
Castamatic
Castamatic sends value_msat_total only on boosts not on streams.
Boost
{
"message": "test",
"app_name": "Castamatic",
"app_version": "8.6.0",
"value_msat_total": 100000,
"url": "https://anchor.fm/s/da6b03c0/podcast/rss",
"podcast": "Test Podcast Anchor",
"action": "boost",
"episode": "this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long episode name!!!",
"episode_guid": "12b4df54-af38-4c53-8099-82f9caacdcd5",
"feedID": 6015671,
"value_msat": 95049,
"ts": 0,
"name": "Alby Test User PUT",
"sender_name": "Alwin_Conshax"
}
Stream
{
"app_name": "Castamatic",
"app_version": "8.6.0",
"url": "https://anchor.fm/s/da6b03c0/podcast/rss",
"podcast": "Test Podcast Anchor",
"action": "stream",
"episode": "this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long episode name!!!",
"episode_guid": "12b4df54-af38-4c53-8099-82f9caacdcd5",
"feedID": 6015671,
"value_msat": 50940,
"ts": 254,
"name": "Test User Peppe the first",
"sender_name": "Alwin_Conshax"
}
Fountain
Fountain sends the itemID as a string, not an int.
Boost
{
"app_name": "Fountain",
"value_msat_total": 100000,
"name": "Alby Test User PUT",
"podcast": "Test Podcast Anchor",
"feedID": 6015671,
"action": "boost",
"sender_id": "nSiq7id78JAdH9uY1pIy",
"sender_name": "@alwin_conshax",
"message": "test",
"itemID": "14934154309",
"boost_link": "https://fountain.fm/episode/14934154309",
"episode": "this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long episode name!!!",
"ts": 15,
"time": "00:00:15"
}
Stream
{
"app_name": "Fountain",
"value_msat_total": 50000,
"name": "Alby Test User PUT",
"podcast": "Test Podcast Anchor",
"feedID": 6015671,
"action": "stream",
"sender_id": "pqwMfFdkCwtj8LKm0tNu",
"sender_name": "@moritz_conshax",
"message": null,
"itemID": "14934154309",
"boost_link": "https://fountain.fm/episode/14934154309",
"episode": "this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long episode name!!!",
"ts": 62,
"time": "00:01:02"
}
Breez
Breez sends the guid on the itemID field and the sender_name is an empty string on streams. Besides that Breez takes its fee from the total instead on top of the total as fountain does, this results in the received value of a split being less than it should calculated from the total.
Boost
{
"podcast": "Test Podcast Anchor",
"episode": "this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long episode name!!!",
"action": "boost",
"time": "00:00:24",
"feedID": 6015671,
"itemID": "12b4df54-af38-4c53-8099-82f9caacdcd5",
"app_name": "Breez",
"value_msat_total": 100000,
"sender_name": "Jade Monkey",
"message": "test"
}
Stream
{
"podcast": "Test Podcast Anchor",
"episode": "this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long episode name!!!",
"action": "stream",
"time": "00:02:37",
"feedID": 6015671,
"itemID": "12b4df54-af38-4c53-8099-82f9caacdcd5",
"app_name": "Breez",
"value_msat_total": 100000,
"sender_name": ""
}