kopia lustrzana https://github.com/Podcastindex-org/podcast-namespace
517 wiersze
21 KiB
Markdown
517 wiersze
21 KiB
Markdown
# The "podcast:value" Specification
|
|
|
|
<small>Version 1.4 by [Dave Jones](https://github.com/daveajones) - with [Gigi](https://github.com/dergigi)
|
|
, [Evan Feenstra](https://github.com/evanfeenstra) & [Paul Itoi](https://github.com/pitoi)</small><br>
|
|
<small>September 1st, 2021</small>
|
|
|
|
<br>
|
|
|
|
## Purpose
|
|
|
|
Here we describe an additional "block" of XML that gives podcasters (and other media creators) a way to receive direct
|
|
payments from their audience in response to the action of viewing or listening to a created work. Utilizing so called "
|
|
Layer 2"
|
|
cryptocurrency networks like Lightning, and possibly other digital currency mechanisms, near real-time micropayments can
|
|
be directly sent from listener or viewer to the creator without the need for a payment processor or other "middle men".
|
|
|
|
The value block designates single or multiple destinations for these micro-payments. The idea is that the creator of the
|
|
media
|
|
describes (within the feed) where and how they would like payments to be sent back to them during consumption
|
|
of that media. The format is designed to be flexible enough to handle many scenarios of direct payment streaming. Even
|
|
the use of fiat currency, if there is an API that is capable of interfacing as a receiver within this format.
|
|
|
|
<br>
|
|
|
|
## Play to Pay
|
|
|
|
This system can be thought of as Play-to-pay, rather than the traditional Pay-to-play paywall approach. When a
|
|
media listener (such as within a podcast app) presses the play button on an episode who's feed contains a value
|
|
block, a compatible app will be expected to begin streaming micro-payments to the designated recipients on a time
|
|
interval that makes sense for the app. The amount of each payment can be any amount the listener chooses, including
|
|
zero. If the listener chooses not to pay to listen to this media, then the app can ignore the value block of that feed.
|
|
|
|
<br>
|
|
|
|
## Payment Intervals
|
|
|
|
The "time interval" for calculating payments is **always 1 minute**. However, the actual interval between when payments
|
|
are sent can be longer. The interval should be chosen with a few factors in mind such as connectivity (is the app
|
|
currently on-line?), transaction fees (batch payments together to reduce fee percentage), cryptocurrency network load
|
|
(can the given crypto network or API support this payment rate?).
|
|
|
|
No matter what the chosen time interval for the actual transaction, the calculation should be done on a once-per-minute
|
|
basis. So, if the micro-payment is sent every 15 minutes, it should be calculated as 15 payments batched together
|
|
in a single transaction. Likewise, some apps with limited connectivity may need to only send payments once per
|
|
hour. In that scenario, there would be 60 payments added up into a single, larger payment. Batching transactions
|
|
like this also helps to minimize the impact of transaction fees on the underlying cryptocurrency network.
|
|
|
|
Note that playback speed is not a factor in this calculation. The "one minute payment interval" refers to the minutes
|
|
that make up the total runtime of the content, thus all payment calculations are independent of playback speed.
|
|
|
|
<div class="page"/>
|
|
|
|
<br><br>
|
|
|
|
## Elements
|
|
|
|
There are two elements that make up what we refer to as the "value block". They are a parent element that specifies the
|
|
currency to use, and one or more child elements that specify who to pay using the currency and protocol described by the
|
|
parent.
|
|
|
|
<br>
|
|
|
|
### Value Element
|
|
|
|
The `<podcast:value>` tag designates the cryptocurrency or payment layer that will be used, the transport method for
|
|
transacting
|
|
the payments, and a suggested amount denominated in the given cryptocurrency.
|
|
|
|
This element can exist at either the `<channel>` or `<item>` level.
|
|
|
|
<br>
|
|
|
|
#### Structure:
|
|
|
|
```xml
|
|
|
|
<podcast:value
|
|
type="[cryptocurrency or layer(string)]"
|
|
method="[payment transport(string)]"
|
|
suggested="[payment amount(float)]"
|
|
>
|
|
[one or more "podcast:valueRecipient" elements]
|
|
</podcast:value>
|
|
```
|
|
|
|
<br>
|
|
|
|
#### Attributes:
|
|
|
|
- `type` (required) This is the service slug of the cryptocurrency or protocol layer.
|
|
- `method` (required) This is the transport mechanism that will be used.
|
|
- `suggested` (optional) This is an optional suggestion on how much cryptocurrency to send with each payment.
|
|
|
|
<br>
|
|
|
|
#### Explanation:
|
|
|
|
Using Lightning as an example, the `type` would be "lightning". Various possible `type` values will be kept
|
|
in a slug list [here](valueslugs.txt). This is the only type currently in active use. Others are under development
|
|
and will be added to the list as they see some measure of adoption, or at least a working example to prove viability.
|
|
|
|
The `method` attribute is used to indicate a sub-protocol to use within the given `type`. Again, returning to
|
|
Lightning as an example, the `method` would be "keysend". Normally, a Lightning payment requires an invoice
|
|
to be generated by the payee in order to fulfill a transaction. The "keysend" protocol of Lightning allows payments
|
|
to be streamed to what is, essentially, an open invoice. Other cryptocurrencies may have a similar protocol that
|
|
would be used here. If not, a value of "default" should be given.
|
|
|
|
The "suggested" amount is just that. It's a suggestion, and must be changeable by the user to another value, or
|
|
to zero. The suggested amount depends on the payment protocol being used. For instance, with Lightning on the Bitcoin
|
|
network, the amount can be as low as one millisatoshi, expressed as `0.00000000001` BTC.
|
|
|
|
A single value tag can contain many `<podcast:valueRecipient>` tags as children. All of these given recipients are
|
|
sent individual payments when the payment interval triggers.
|
|
|
|
The value tag, when it exists at the `<channel>` level, designates the payment scheme for the entire podcast. When it
|
|
exists at the `<item>` level, it is intended to override the channel level tag for that episode only.
|
|
|
|
#### Example:
|
|
|
|
The following example uses the `keysend` method of the `lightning` network and
|
|
sets the suggested value to 5 sats. A sat, short for satoshi, is a
|
|
one-hundred-millionth of a single bitcoin (0.00000001 BTC). The smallest unit on
|
|
the Lightning Network is a millisat, which is a thousandth of a sat.
|
|
|
|
```xml
|
|
|
|
<podcast:value
|
|
type="lightning"
|
|
method="keysend"
|
|
suggested="0.00000005000"
|
|
></podcast:value>
|
|
```
|
|
|
|
<br><br>
|
|
|
|
<div class="page"/>
|
|
|
|
### Value Recipient Element
|
|
|
|
The `valueRecipient` tag designates various destinations for payments to be sent to during consumption of the enclosed
|
|
media. Each recipient is considered to receive a "split" of the total payment according to the number of shares given
|
|
in the `split` attribute.
|
|
|
|
This element may only exist within a parent `<podcast:value>` element.
|
|
|
|
There is no limit on how many `valueRecipient` elements can be present in a given `<podcast:value>` element.
|
|
|
|
<br>
|
|
|
|
#### Structure:
|
|
|
|
```xml
|
|
|
|
<podcast:valueRecipient
|
|
name="[name of recipient(string)]"
|
|
type="[address type(string)]"
|
|
address="[the receiving address(string)]"
|
|
customKey="[optional key to pass(mixed)]"
|
|
customValue="[optional value to pass(mixed)]"
|
|
split="[share count(int)]"
|
|
fee=[true|false]
|
|
/>
|
|
```
|
|
|
|
<br>
|
|
|
|
#### Attributes:
|
|
|
|
- `name` (recommended) A free-form string that designates who or what this recipient is.
|
|
- `customKey` (optional) The name of a custom record key to send along with the payment.
|
|
- `customValue` (optional) A custom value to pass along with the payment. This is considered the value that belongs to
|
|
the `customKey`.
|
|
- `type` (required) A slug that represents the type of receiving address that will receive the payment.
|
|
- `address` (required) This denotes the receiving address of the payee.
|
|
- `split` (required) The number of shares of the payment this recipient will receive.
|
|
- `fee` (optional) If this attribute is not specified, it is assumed to be false.
|
|
|
|
<br>
|
|
|
|
#### Explanation:
|
|
|
|
The `name` is just a human readable description of who or what this payment destination is. This could be something
|
|
simple like
|
|
"Podcaster", "Co-host" or "Producer". It could also be more descriptive like "Ronald McDonald House Charity", if a
|
|
podcaster
|
|
chooses to donate a percentage of their incoming funds to a charity.
|
|
|
|
The `type` denotes what type of receiving entity this is. For instance, with lightning this would typically be "node".
|
|
This would
|
|
indicate that the `address` attribute for this recipient is a Lightning node that is capable of directly receiving
|
|
incoming keysend payments. Valid values for
|
|
the `type` attribute are kept in the accompanying file [here](valueslugs.txt). Another option is given in examples
|
|
below.
|
|
|
|
Payments must be sent to a valid destination which is given in the `address` attribute. This address format will vary
|
|
depending on
|
|
the underlying currency being used.
|
|
|
|
The `split` attribute denotes an amount of "shares" of the total payment that the recipient will receive when each timed
|
|
payment is made.
|
|
When a single `<podcast:valueRecipient>` is present, it should be assumed that the `split` for that recipient is 100%,
|
|
and the "split" should
|
|
be ignored. When multiple recipients are present, a share calculation (see below) should be made to determine how much
|
|
to send to each recipient's address.
|
|
|
|
The `fee` attribute tells apps whether this split should be treated as a "fee", or a normal split. If this attribute is
|
|
true, then this split should be calculated
|
|
as a fee, meaning its percentage (as calculated from the shares) should be taken off the top of the entire transaction
|
|
amount. This is the preferred way for service
|
|
providers such as apps, hosting companies, API's and third-party value add providers to add their fee to a value block.
|
|
|
|
#### Custom Key/Value Pairs
|
|
|
|
The `customKey` and `customValue` pair can be used (especially for the Lighning Network) to help a receiving application
|
|
route or process payments that have all arrived at one node.
|
|
|
|
The idea is that Podcast Index will parse and store and all client apps will always send a `customKey:customValue` pair
|
|
if these are found in the Value Block.
|
|
|
|
For example, the `customKey`'s of `818818`, `112111100` are used to route payments to Hive accounts or specific wallets
|
|
on LNPay respectively. These fields are
|
|
documented [in the list maintained by Satoshis Stream](https://github.com/satoshisstream/satoshis.stream/blob/main/TLV_registry.md)
|
|
.
|
|
|
|
If your specific application would benefit from your own `customKey:customValue` pair which will be passed along from
|
|
the player to your app, and for which nothing already exists, add your own.
|
|
|
|
<br><br>
|
|
|
|
<div class="page"/>
|
|
|
|
### Payment calculation
|
|
|
|
The interval payment calculation is:
|
|
|
|
(Number of shares / Share total) * Interval payout * Interval count
|
|
|
|
To calculate payouts, let's take the following value block as an example:
|
|
|
|
```xml
|
|
|
|
<podcast:value type="lightning" method="keysend" suggested="0.00000015000">
|
|
<podcast:valueRecipient
|
|
name="Host"
|
|
type="node"
|
|
address="02d5c1bf8b940dc9cadca86d1b0a3c37fbe39cee4c7e839e33bef9174531d27f52"
|
|
split="50"
|
|
/>
|
|
<podcast:valueRecipient
|
|
name="Co-Host"
|
|
type="node"
|
|
address="032f4ffbbafffbe51726ad3c164a3d0d37ec27bc67b29a159b0f49ae8ac21b8508"
|
|
split="40"
|
|
/>
|
|
<podcast:valueRecipient
|
|
name="Producer"
|
|
type="node"
|
|
address="03ae9f91a0cb8ff43840e3c322c4c61f019d8c1c3cea15a25cfc425ac605e61a4a"
|
|
split="10"
|
|
/>
|
|
</podcast:value>
|
|
```
|
|
|
|
This block designates three payment recipients. On each timed payment interval, the total payment will be split into 3
|
|
smaller
|
|
payments according to the shares listed in the split for each recipient. So, in this case, if the listener decided to
|
|
pay 100 sats per minute for listening
|
|
to this podcast, then once per minute the "Host" would be sent 50 sats, the "Co-Host" would be sent 40 sats and the
|
|
"Producer" would be sent 10 sats - all to their respective lightning node addresses using the "keysend" protocol.
|
|
|
|
If, instead of a 50/40/10 (total of 100) split, the splits were given as 190/152/38 (total of 380), the respective
|
|
payment amounts each minute would still
|
|
be 50 sats, 40 sats and 10 sats because the listener chose to pay 100 sats per minute, and the respective shares (as a
|
|
percentage of the total) would remain the same.
|
|
|
|
On a 190/152/38 split, each minute the payment calculation would be:
|
|
|
|
- Interval payout: 100 sats
|
|
|
|
- Share total: 380
|
|
|
|
- Recipient #1 gets a payment of: 50 sats (190 / 380 * 100)
|
|
- Recipient #2 gets a payment of: 40 sats (152 / 380 * 100)
|
|
- Recipient #3 gets a payment of: 10 sats (38 / 380 * 100)
|
|
|
|
If an app chooses to only make a payout once every 30 minutes of listening/watching, the calculation would be the same
|
|
after multiplying
|
|
the per-minute payment by 30:
|
|
|
|
- Interval payout: 3000 sats (100 * 30)
|
|
|
|
- Shares total: 380
|
|
|
|
- Recipient #1 gets a payment of: 1500 sats (190 / 380 * 3000)
|
|
- Recipient #2 gets a payment of: 1200 sats (152 / 380 * 3000)
|
|
- Recipient #3 gets a payment of: 300 sats (38 / 380 * 3000)
|
|
|
|
As shown above, the once per minute calculation does not have to actually be sent every minute. A longer payout period
|
|
can be chosen. But,
|
|
the once-per-minute nature of the payout still remains in order for listeners and creators to have an easy way to
|
|
measure and calculate how much
|
|
they will spend and charge.
|
|
|
|
<br><br>
|
|
|
|
<div class="page"/>
|
|
|
|
### Supported Currencies and Protocols
|
|
|
|
The value block is designed to be flexible enough to handle most any cryptocurrency, and even fiat currencies with a
|
|
given
|
|
API that exposes a compatible process.
|
|
|
|
Currently, development is centered mostly on [Lightning](https://github.com/lightningnetwork) using the "keysend"
|
|
method. Keysend allows for push
|
|
based payments without the recipient needing to generate an invoice to receive them.
|
|
Another method to send spontaneous payments via Lightning is AMP, atomic
|
|
[multipath payments][AMP]. AMP supersedes keysend in many ways and allows for
|
|
more robust and larger payments. However, it is still in beta and thus not
|
|
widely implemented as of now.
|
|
|
|
[AMP]: https://bitcoinops.org/en/topics/multipath-payments/
|
|
|
|
<br>
|
|
|
|
#### Lightning
|
|
|
|
For the `<podcast:value>` tag, the following attributes MUST be used:
|
|
|
|
- `type` (required): "lightning"
|
|
- `method` (required): "keysend" or "amp"
|
|
- `suggested` (optional): A float representing a BTC amount.
|
|
e.g. 0.00000005000 is 5 Sats.
|
|
|
|
For the `<podcast:valueRecipient>` tag, the following attributes MUST be used:
|
|
|
|
- `type`: "node"
|
|
- `address`: \<the destination node's pubkey\>
|
|
- `split`: \<the number of shares\>
|
|
|
|
If the receiving Lightning node, or service, requires application specific data to be sent with the payment in the
|
|
lightning message `extension` (a _TLV stream_, see the Appendix section), the `customKey` and `customValue` can be
|
|
utilized as follows:
|
|
|
|
- `type`: "node"
|
|
- `method`: "keysend" or "amp"
|
|
- `customKey`: \<tlv record type, a 64 bit integer greater than or equal to 2^16\>
|
|
- `customValue`: \<tlv record value, a string\>
|
|
- `address`: \<the destination node's pubkey\>
|
|
- `split`: \<the number of shares\>
|
|
|
|
When sending a payment containing application specific data, the client must use UTF-8 as encoding for `customValue`.
|
|
|
|
**Remarks:**
|
|
|
|
- `customValue` is specified as a string due to the emergence of known users for this field (see Appendix). If we decide
|
|
to support raw binary data in the future, a new attribute can be introduced to indicate the different behavior
|
|
- There is at least one known shared node ([satoshis.stream](https://satoshis.stream/)) that requires, in addition to
|
|
this specification, the inclusion of the TLV record with type `7629169`, as
|
|
defined [here](blip-0010.md), in order to correctly route the payment to the corresponding receiver
|
|
|
|
<br>
|
|
|
|
##### Example
|
|
|
|
This is a live, working example of a Lightning keysend value block in production. It designates four recipients for
|
|
payment - two
|
|
podcast hosts at 49 and 46 shares respectively, a producer working on per episode chapter creation who gets a 5 share,
|
|
and
|
|
a single share (effectively 1%) fee to the Podcastindex.org API.
|
|
Since the value block is defined at the `<channel>` level, it applies to every podcast episode.
|
|
|
|
```xml
|
|
...
|
|
<channel>
|
|
<podcast:value type="lightning" method="keysend" suggested="0.00000015000">
|
|
<podcast:valueRecipient
|
|
name="Adam Curry (Podcaster)"
|
|
type="node"
|
|
address="02d5c1bf8b940dc9cadca86d1b0a3c37fbe39cee4c7e839e33bef9174531d27f52"
|
|
split="49"
|
|
/>
|
|
<podcast:valueRecipient
|
|
name="Dave Jones (Podcaster)"
|
|
type="node"
|
|
address="032f4ffbbafffbe51726ad3c164a3d0d37ec27bc67b29a159b0f49ae8ac21b8508"
|
|
split="46"
|
|
/>
|
|
<podcast:valueRecipient
|
|
name="Dreb Scott (Chapter Creation)"
|
|
type="node"
|
|
address="02dd306e68c46681aa21d88a436fb35355a8579dd30201581cefa17cb179fc4c15"
|
|
split="5"
|
|
/>
|
|
<podcast:valueRecipient
|
|
name="Podcastindex.org (Donation)"
|
|
type="node"
|
|
address="03ae9f91a0cb8ff43840e3c322c4c61f019d8c1c3cea15a25cfc425ac605e61a4a"
|
|
split="1"
|
|
fee="true"
|
|
/>
|
|
</podcast:value>
|
|
...
|
|
<item>...</item>
|
|
<item>...</item>
|
|
...
|
|
</channel>
|
|
```
|
|
|
|
To use Atomic Multipath Payments (AMP) instead of `keysend`, simply set the
|
|
payment `method` to `amp`:
|
|
|
|
```xml
|
|
...
|
|
<channel>
|
|
<podcast:value type="lightning" method="amp" suggested="0.00000015000">
|
|
...
|
|
</podcast:value>
|
|
</channel>
|
|
```
|
|
|
|
##### Example: `<Item>` Override
|
|
|
|
To set up different payment splits for individual episodes, a value block has to
|
|
be defined on the `<item>` level. This will override the value settings set on
|
|
the `<channel>` level.
|
|
|
|
The following example defines different value blocks for each episode in order
|
|
to include the guests as value recipients. Payments are split 50/50 between host
|
|
and guest.
|
|
|
|
```xml
|
|
...
|
|
<channel>
|
|
<podcast:value type="lightning" method="keysend" suggested="0.00000021000">
|
|
<podcast:valueRecipient
|
|
name="John Vallis (Host)"
|
|
type="node"
|
|
address="02a9cd2bca29dd7e29bdfdf485a8e78b8ccf9327517afa03a59be8f62a58792e1b"
|
|
split="100"
|
|
/>
|
|
</podcast:value>
|
|
...
|
|
<item>
|
|
<title>#00 - John's Solo Episode</title>
|
|
...
|
|
</item>
|
|
<item>
|
|
<title>#01 - John and Gigi</title>
|
|
<podcast:value type="lightning" method="keysend" suggested="0.00000021000">
|
|
<podcast:valueRecipient
|
|
name="John Vallis (Host)"
|
|
type="node"
|
|
address="02a9cd2bca29dd7e29bdfdf485a8e78b8ccf9327517afa03a59be8f62a58792e1b"
|
|
split="50"
|
|
/>
|
|
<podcast:valueRecipient
|
|
name="Gigi (Guest)"
|
|
type="node"
|
|
address="02e12fea95f576a680ec1938b7ed98ef0855eadeced493566877d404e404bfbf52"
|
|
split="50"
|
|
/>
|
|
</podcast:value>
|
|
...
|
|
</item>
|
|
<item>
|
|
<title>#02 - John and Paul</title>
|
|
<podcast:value type="lightning" method="keysend" suggested="0.00000021000">
|
|
<podcast:valueRecipient
|
|
name="John Vallis (Host)"
|
|
type="node"
|
|
address="02a9cd2bca29dd7e29bdfdf485a8e78b8ccf9327517afa03a59be8f62a58792e1b"
|
|
split="50"
|
|
/>
|
|
<podcast:valueRecipient
|
|
name="Paul Itoi (Guest)"
|
|
type="node"
|
|
address="03a9a8d953fe747d0dd94dd3c567ddc58451101e987e2d2bf7a4d1e10a2c89ff38"
|
|
split="50"
|
|
/>
|
|
</podcast:value>
|
|
...
|
|
</item>
|
|
...
|
|
</channel>
|
|
```
|
|
|
|
<br>
|
|
|
|
### Appendix A - TLV Records and Extensions
|
|
|
|
Lightning payments are performed using lightning messages as specified
|
|
in [BOLT #1: Base Protocol](https://github.com/lightningnetwork/lightning-rfc/blob/master/01-messaging.md).
|
|
|
|
One part of the message is the `extension`, a TLV (Type-Length-Value) stream. Podcast specific data can be added to
|
|
transactions using the key **7629169** with the value described in [bLIP 10](blip-0010.md)
|
|
|
|
A community maintained registry of additional known custom record types and formats, governed by satoshis.stream, can be
|
|
found at the document [TLV record registry](https://github.com/satoshisstream/satoshis.stream/blob/main/TLV_registry.md)
|
|
. In special, the section _Fields used in customKey / customValue Pairs_ documents the known use cases for
|
|
the `customKey` and `customValue` attributes.
|
|
|
|
<br>
|
|
|
|
### Appendix B - Payment Actions
|
|
|
|
There are currently 3 payment "actions" that are defined in the BLIP-10 TLV extension that is embedded in the payment
|
|
payload: "stream", "boost" and "auto".
|
|
|
|
* `stream` - This means the payment is a timed interval payment (i.e. - every minute) that is sent or queued while the
|
|
user is engaged in active listening/viewing of the content.
|
|
* `boost` - This means the payment is a user generated one-time payment that happens in response to a user initiated
|
|
action like a "Boost" button push, or some other clearly labeled payment initiation function. These
|
|
types of payments can contain textual messages (i.e. - a boostagram).
|
|
* `auto` - This means the payment was an app initiated payment that recurs at a specific long-form interval such as
|
|
weekly, monthly, yearly, etc. The user configures an interval and payment amount in the app and the app
|
|
then sends that amount at the specified time when each interval passes. |