upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/vending-machine.md
diff options
context:
space:
mode:
authorpablof7z <p@f7z.io>2023-07-23 21:28:59 +0200
committerpablof7z <p@f7z.io>2023-07-23 21:28:59 +0200
commit53bd97373b5442445f5c387945cf5685ac39ab80 (patch)
tree0a69f8e1641082d6707512051c7be4cd081e5da5 /vending-machine.md
parente9924bdcde2dc516a891c027bd023f487b73a674 (diff)
use different kinds per job request type
Diffstat (limited to 'vending-machine.md')
-rw-r--r--vending-machine.md217
1 files changed, 127 insertions, 90 deletions
diff --git a/vending-machine.md b/vending-machine.md
index 1d08bd2..4dbc5a3 100644
--- a/vending-machine.md
+++ b/vending-machine.md
@@ -8,10 +8,19 @@ Data Vending Machine
8 8
9This NIP defines the interaction between customers and Service Providers to perform on-demand computation. 9This NIP defines the interaction between customers and Service Providers to perform on-demand computation.
10 10
11## Kinds
12This NIP reserves the range `65000-69999` for data vending machine use.
13
14| Kind | Description |
15| ---- | ----------- |
16| 65000 | Job feedback |
17| 65001 | Job result |
18| 65002-69999 | Job request kinds |
19
11## Rationale 20## Rationale
12Nostr can act as a marketplace for data processing, where users request jobs to be processed in certain ways (e.g. "speech-to-text", "summarization", etc.), but where they don't necessarily care about "who" processes the data. 21Nostr can act as a marketplace for data processing, where users request jobs to be processed in certain ways (e.g. "speech-to-text", "summarization", etc.), but where they don't necessarily care about "who" processes the data.
13 22
14This NIP is not to be confused with a 1:1 marketplace; but rather, a flow where user announces a desired output, willigness to pay, and service providers compete to fulfill the job requirement in the best way possible. 23This NIP is not to be confused with a 1:1 marketplace; but rather, a flow where user announces a desired output, willingness to pay, and service providers compete to fulfill the job requirement in the best way possible.
15 24
16### Actors 25### Actors
17There are two actors to the workflow described in this NIP: 26There are two actors to the workflow described in this NIP:
@@ -24,52 +33,44 @@ A request to have data processed -- published by a customer
24 33
25```json 34```json
26{ 35{
27 "kind": 68001, 36 "kind": 6xxxx,
28 "content": "", 37 "content": "",
29 "tags": [ 38 "tags": [
30 [ "j", "<job-type>", "<optional-model>" ], 39 [ "i", "<data>", "<input-type>", "<marker>", "<relay>" ],
31 [ "i", "<data>", "<input-type>", "<marker>" ],
32 [ "output", "<mime-type>" ], 40 [ "output", "<mime-type>" ],
33 [ "relays", "wss://..."], 41 [ "relays", "wss://..."],
34 [ "bid", "<msat-amount>", "<optional-max-price>" ], 42 [ "bid", "<msat-amount>" ],
35 [ "exp", "<timestamp>" ], 43 [ "exp", "<timestamp>" ],
36 [ "p", "service-provider-1" ], 44 [ "t", "bitcoin" ]
37 [ "p", "service-provider-2" ],
38 ] 45 ]
39} 46}
40``` 47```
41 48
42* `content` field: An optional, human-readable description of what this job is for. 49* `i` tag: Input data for the job, (zero or more inputs may exist)
43* `j` tag: Job-type to be executed.
44 * A job request MUST have exactly one `j` tag.
45 * It MAY include a second value specifying the name of a model to be used when computing the result.
46
47* `i` tag: Input data for the job.
48 * A job request CAN have zero or more inputs.
49 * Positional arguments: `["i", "<data>", "<input-type>", "<relay>", "<marker>"]`
50 * `<data>`: The argument for the input 50 * `<data>`: The argument for the input
51 * `<input-type>`: The way this argument should be interpreted, one of: 51 * `<input-type>`: The way this argument should be interpreted, MUST be one of:
52 * `url`: a URL to be fetched 52 * `url`: a URL to be fetched
53 * `event`: a nostr event ID, include an optional relay-url extra param 53 * `event`: a nostr event ID, include an optional relay-url extra param
54 * `job`: the output of a previous job with the specified event ID 54 * `job`: the output of a previous job with the specified event ID
55 * `<relay>`: if `event` or `job` input-type, the relay where the event/job was published, otherwise optional or empty string. 55 * `content`:
56 * `<marker>`: an optional field indicating how this input should be used. 56 * `<marker>`: an optional field indicating how this input should be used.
57* `output` tag: MIME type. Expected output format. Service Providers SHOULD publish the result of the job in this format. 57 * `<relay>`: if `event` or `job` input-type, the relay where the event/job was published, otherwise optional or empty string.
58* `bid` tag: Customer MAY specify a maximum amount (in millisats) they are willing to pay. 58* `output` tag (opt): MIME type. Expected output format. Service Providers SHOULD publish the result of the job in this format if it has been specified.
59* `bid` tag (opt): Customer MAY specify a maximum amount (in millisats) they are willing to pay.
59* `relays` tag: relays where Service Providers SHOULD publish responses to. 60* `relays` tag: relays where Service Providers SHOULD publish responses to.
60* `p` tags: Service Providers the customer is interested in having process this job. Other SP MIGHT still choose to process the job. 61* `p` tags (opt): Service Providers the customer is interested in having process this job. Other SP MIGHT still choose to process the job.
61* `exp`: Optional expiration timestamp. Service Providers SHOULD not send results after this timestamp. 62* `exp` (opt): expiration timestamp. Service Providers SHOULD not send results after this timestamp.
62 63
63## Job result 64## Job result
64The output of processing the data -- published by the Service Provider. 65The output of processing the data -- published by the Service Provider.
65```json 66```json
66{ 67{
67 "pubkey": "service-provider pubkey in hex", 68 "pubkey": "<service-provider pubkey>",
68 "content": "<payload>", 69 "content": "<payload>",
69 "kind": 68002, 70 "kind": 65001,
70 "tags" [ 71 "tags" [
71 [ "request", "<68001-event>" ], 72 [ "request", "<job-request>" ],
72 [ "e", "<id-of-68001-event>" ], 73 [ "e", "<job-request-id>", "<relay-hint>" ],
73 [ "p", "<Customer's pubkey>" ], 74 [ "p", "<Customer's pubkey>" ],
74 [ "status", "success", "<more-info>" ], 75 [ "status", "success", "<more-info>" ],
75 [ "amount", "requested-payment-amount", "<optional-bolt11>" ] 76 [ "amount", "requested-payment-amount", "<optional-bolt11>" ]
@@ -77,6 +78,9 @@ The output of processing the data -- published by the Service Provider.
77} 78}
78``` 79```
79 80
81* `request` tag: The job request event ID.
82* `amount`: millisats that the Service Provider is requesting to be paid. An optional third value can be a bolt11 invoice.
83
80## Job feedback 84## Job feedback
81Both customers and service providers can give feedback about a job. 85Both customers and service providers can give feedback about a job.
82 86
@@ -86,29 +90,25 @@ The result of the job SHOULD be included in the `content` field.
86* `amount`: millisats that the Service Provider is requesting to be paid. An optional third value can be a bolt11 invoice. 90* `amount`: millisats that the Service Provider is requesting to be paid. An optional third value can be a bolt11 invoice.
87 91
88# Protocol Flow 92# Protocol Flow
89* Customer publishes a job request 93* Customer publishes a job request (e.g. `kind:65002`).
90`{ "kind": 68001, "tags": [ [ "j", "speech-to-text" ], ... ] }` 94* Service Prpvoders can submit `kind:65000` job-feedback events (e.g. `payment-required`, `processing`, `error`, etc.).
91* Service Prpvoders can submit `kind:68003` job-feedback events (e.g. `payment-required`, `processing`, `unprocessable-entity`, etc.). 95* Upon completion, the service provider publishes the result of the job with a `kind:65001` job-result event.
92* Upon completion, the service provider publishes the result of the job with a `kind:68002` job-result event. 96* At any point, the user can pay the included `bolt11` or zap any of the events the service provider has sent to the user.
93* Upon acceptance, the user zaps the service provider, tagging the job result event with a `kind:7` 👍 reaction.
94 97
95`kind:68002` and `kind:68003` events MAY include an `amount` tag, this can be interpreted as a suggestion to pay. Service Providers 98`kind:65000` and `kind:65001` events MAY include an `amount` tag, this can be interpreted as a suggestion to pay. Service Providers SHOULD use the `payment-required` feedback event to signal that a payment is required and no further actions will be performed until the payment is sent. Custeroms are can always either pay the included `bolt11` invoice or zap the event requesting the payment and service providers should monitor for both if they choose to include a bolt11 invoice.
96SHOULD use the `payment-required` feedback event to signal that a payment must be done before moving on to the next step.
97 99
98## Notes about the protocol flow 100## Notes about the protocol flow
99The flow is deliverately left ambiguos, allowing vast flexibility for the interaction between customers and service providers so that 101The flow is deliverately ambiguos, allowing vast flexibility for the interaction between customers and service providers so that service providers can model their behavior based on their own decisions. Some service providers might choose to submit a `payment-required` as the first reaction before sending an `processing` or before delivering `kind:65001` results, some might choose to serve partial results for the job (e.g. as a sample), send a `payment-required`to deliver the rest of the results, and some service providers might choose to assess likelyhood of payment based on an npub's past behavior and thus serve the job results before requesting payment for the best possible UX.
100service providers can model their behavior based on their own decisions. Some service providers might choose to submit a `payment-required` 102
101as the first reaction before sending an `processing` or before delivering `kind:68002` results, some might choose to serve partial results 103It's not up to this NIP to define how individual vending machines should choose to run their business.
102for the job (e.g. as a sample), send a `payment-required`to deliver the rest of the results, and some service providers might choose to
103assess likelyhood of payment based on an npub's past behavior and thus serve the job results before requesting payment for the best possible UX.
104 104
105# Payment 105# Payment
106Customers SHOULD pay service providers whose job results they accept by zapping the Service Provider and tagging the `kind:68002` job result. 106Customers SHOULD pay service providers whose job results they accept by either zapping the Service Provider and tagging the `kind:65001` job result or, if included, paying the bolt11 invoice.
107 107
108Additionally, if a service provider requests full or partial prepayment via a `kind:68003` job-feedback event, the customer SHOULD zap that event to pay the service provider. 108Additionally, if a service provider requests full or partial prepayment via a `kind:65000` job-feedback event, the customer SHOULD zap that event to pay the service provider.
109 109
110# Cancellation 110# Cancellation
111A `kind:68001` job request might be cancelled by publishing a `kind:5` delete request event tagging the job request event. 111A job request might be cancelled by publishing a `kind:5` delete request event tagging the job request event.
112 112
113# Job chaining 113# Job chaining
114A Customer MAY request multiple jobs to be processed in a chained form, so that the output of a job can be the input of the next job. (e.g. summarization of a podcast's transcription). This is done by specifying as `input` an eventID of a different job with the `job` marker. 114A Customer MAY request multiple jobs to be processed in a chained form, so that the output of a job can be the input of the next job. (e.g. summarization of a podcast's transcription). This is done by specifying as `input` an eventID of a different job with the `job` marker.
@@ -116,17 +116,31 @@ A Customer MAY request multiple jobs to be processed in a chained form, so that
116Service Providers MAY begin processing a subsequent job the moment they see the prior job's result, but they will likely wait for a zap to be published first. This introduces a risk that Service Provider of job #1 might delay publishing the zap event in order to have an advantage. This risk is up to Service Providers to mitigate or to decide whether the service provider of job #1 tends to have good-enough results so as to not wait for a explicit zap to assume the job was accepted. 116Service Providers MAY begin processing a subsequent job the moment they see the prior job's result, but they will likely wait for a zap to be published first. This introduces a risk that Service Provider of job #1 might delay publishing the zap event in order to have an advantage. This risk is up to Service Providers to mitigate or to decide whether the service provider of job #1 tends to have good-enough results so as to not wait for a explicit zap to assume the job was accepted.
117 117
118# Job Feedback 118# Job Feedback
119The parties to a job request can use `kind:65000` to provide feedback about the job, using a `status` tag to indicate the type of feedback.
119 120
120## Job request reactions 121Any job feedback event MIGHT include an `amount` tag, indicating the amount of millisats the party is requesting to be paid. An optional third value can be a bolt11 invoice.
121Service Providers might opt to give feedback about a job.
122 122
123### E.g. Payment required 123| status | description |
124|--------|-------------|
125| `payment-required` | Service Provider requires payment before continuing. |
126| `processing` | Service Provider is processing the job. |
127| `error` | Service Provider was unable to process the job. |
128| `success` | Service Provider successfully processed the job. |
129| `failure` | Service Provider failed to process the job. |
130| `partial` | Service Provider partially processed the job. The `.content` might include a sample of the partial results. |
131
132Any job feedback event MIGHT include an `amount` tag, as described in the [Job Result](#job-result) section.
133
134Any job feedback event MIGHT include results in the `.content` field, as described in the [Job Result](#job-result) section.
135
136### E.g. Payment required (with sample content)
124```json 137```json
125{ 138{
126 "kind": 68003, 139 "kind": 65000,
127 "content": "Please pay 7 sats for job xxxx", 140 "content": "This is the transcription service that you",
128 "tags": [ 141 "tags": [
129 [ "e", <job-request-id> ], 142 [ "e", <job-request-id>, <relay-hint> ],
143 [ "p", <customer-pubkey> ],
130 [ "status", "payment-required" ], 144 [ "status", "payment-required" ],
131 [ "amount", "7000" ], 145 [ "amount", "7000" ],
132 ] 146 ]
@@ -144,153 +158,160 @@ Service providers are at obvious risk of having their results not compensated. M
144 158
145It's out of scope (and undesirable) to have this NIP address this issue; the market should. 159It's out of scope (and undesirable) to have this NIP address this issue; the market should.
146 160
161### Encrypted job requests
162Not to be included in the first draft of this NIP, but encrypted job requests should be added. A few ways:
163* publish job requests with some useful metadata of the job "e.g. length of audio to be transcribed", service providers offer to do the job, the customer replies with a NIP-04-like encrypted job requested encrypted with the service provider's pubkey.
164
147# Appendix 1: Examples 165# Appendix 1: Examples
148 166
149## Transcript of a podcast from second `900` to `930`. 167## Transcript of a podcast from second `900` to `930`.
150 168
151### `kind:68001`: Job Request 169### `kind:65002`: Speech-to-text job request
152```json 170```json
153{ 171{
154 "id": "12345", 172 "id": "12345",
155 "pubkey": "abcdef", 173 "pubkey": "abcdef",
156 "content": "", 174 "content": "",
175 "kind": 65002,
157 "tags": [ 176 "tags": [
158 [ "j", "speech-to-text" ],
159 [ "i", "https://bitcoin.review/episode1.mp3", "url" ], 177 [ "i", "https://bitcoin.review/episode1.mp3", "url" ],
160 [ "params", "range", "900", "930" ], 178 [ "params", "range", "900", "930" ],
161 [ "bid", "5000", "9000" ], 179 [ "output", "text/vtt" ],
180 [ "bid", "50000" ],
162 [ "output", "text/plain" ] 181 [ "output", "text/plain" ]
163 ] 182 ]
164} 183}
165``` 184```
166 185
167### `kind:68003`: Job Feedback: request for (partial) payment 186### `kind:65001`: Job Feedback: request for (partial) payment
187* The SP is signaling here that it won't start processing until 100 sats are paid
168```json 188```json
169{ 189{
170 "kind": 68003, 190 "kind": 65000,
171 "content": "", 191 "content": "",
172 "tags": [ 192 "tags": [
173 ["e", "12345"], 193 ["e", "12345"],
174 ["p", "abcdef"], 194 ["p", "abcdef"],
175 ["status", "payment-required"], 195 ["status", "payment-required"],
176 ["amount", "1000"] 196 ["amount", "100000"]
177 ] 197 ]
178} 198}
179``` 199```
180 200
181* User zaps 1000 sats to event kind:68003. 201* User zaps 100 sats to the `kind:65000` job-feedback
182 202
183### `kind:68002`: Job fulfillment + request for remaining payment 203### `kind:65001`: Job result + request for remaining payment
184```json 204```json
185{ 205{
186 "content": "blah blah blah", 206 "content": "blah blah blah",
187 "tags": [ 207 "tags": [
188 ["e", "12345"], 208 ["e", "12345"],
189 ["p", "abcdef"], 209 ["p", "abcdef"],
190 ["amount", "6000"] 210 ["amount", "400000"]
191 ] 211 ]
192} 212}
193``` 213```
194 214
195## Summarization of a podcast 215## Summarization of a podcast
216User publishes two job requests at the same time.
196 217
197User publishes two job requests at the same time in the order they should be executed. 218### `kind:65002`: Job Request #1: speech-to-text
198
199### `kind:68001`: Job Request #1
200```json 219```json
201{ 220{
202 "id": "12345", 221 "id": "12345",
203 "pubkey": "abcdef", 222 "pubkey": "abcdef",
223 "kinds" 65002,
204 "content": "I need a transcript of Bitcoin.review from second 900 to 930", 224 "content": "I need a transcript of Bitcoin.review from second 900 to 930",
205 "tags": [ 225 "tags": [
206 [ "j", "speech-to-text" ],
207 [ "params", "range", "900", "930" ],
208 [ "i", "https://bitcoin.review/episode1.mp3", "url" ], 226 [ "i", "https://bitcoin.review/episode1.mp3", "url" ],
209 [ "bid", "5000", "9000" ] 227 [ "output", "text/plain" ],
228 [ "params", "range", "900", "930" ],
229 [ "bid", "100000" ]
210 ] 230 ]
211} 231}
212``` 232```
213 233
214### `kind:68001`: Job Request #2 234### `kind:65003`: Job Request #2: summarization of job #1's result
215```json 235```json
216{ 236{
217 "id": "12346", 237 "id": "12346",
218 "pubkey": "abcdef", 238 "pubkey": "abcdef",
239 "kinds": 65003,
219 "content": "", 240 "content": "",
220 "tags": [ 241 "tags": [
221 [ "j", "summarization" ],
222 [ "params", "length", "3 paragraphs" ],
223 [ "i", "12346", "job" ], 242 [ "i", "12346", "job" ],
224 [ "bid", "300", "900" ] 243 [ "output", "text/plain" ],
244 [ "params", "length", "3 paragraphs" ],
245 [ "bid", "10000" ]
225 ] 246 ]
226} 247}
227``` 248```
228 249
229## Translation of a note 250## Translation of a note
230### `kind:68001`: Job Request #1 251### `kind:65004`: Job Request #1: translation of an existing note
231```json 252```json
232{ 253{
233 "id": "12346", 254 "id": "12346",
234 "pubkey": "abcdef", 255 "pubkey": "abcdef",
235 "content": "", 256 "content": "",
257 "kinds": 65004,
236 "tags": [ 258 "tags": [
237 [ "j", "translation" ],
238 [ "i", "<hexid>", "event", "wss://relay.nostr.com" ] 259 [ "i", "<hexid>", "event", "wss://relay.nostr.com" ]
239 [ "params", "language", "es_AR" ], 260 [ "params", "lang", "es_AR" ],
240 [ "bid", "100", "500" ] 261 [ "bid", "5000" ]
241 ] 262 ]
242} 263}
243``` 264```
244 265
245### `kind:68003`: Job respomse 266### `kind:65001`: Job respomse
246```json 267```json
247{ 268{
248 "kind": 68003, 269 "kind": 65001,
249 "content": "Che, que copado, boludo!", 270 "content": "Che, que copado, boludo!",
250 "tags": [ 271 "tags": [
251 ["e", "12346"], 272 ["e", "12346"],
252 ["p", "abcdef"], 273 ["p", "abcdef"],
253 ["amount", "1000"] 274 ["amount", "4000"]
254 ] 275 ]
255} 276}
256``` 277```
257 278
258## AI-image of the summarization of 2 podcasts 279## AI-image of the summarization of 2 podcasts
259 280
260### `kind:68001`: Job request #1 (transcribe podcast #1) 281### `kind:65002`: Job request #1 (transcribe podcast #1)
261```json 282```json
262{ 283{
263 "id": "123", 284 "id": "123",
285 "kind" 65002,
264 "tags": [ 286 "tags": [
265 [ "j", "speech-to-text" ],
266 [ "i", "https://example.com/episode1.mp3", "url" ], 287 [ "i", "https://example.com/episode1.mp3", "url" ],
267 [ "bid", "100", "500" ] 288 [ "bid", "100000" ]
268 ] 289 ]
269} 290}
270``` 291```
271 292
272### `kind:68001`: Job request #2 (transcribe podcast #2) 293### `kind:65002`: Job request #2 (transcribe podcast #2)
273```json 294```json
274{ 295{
275 "id": "124", 296 "id": "124",
297 "kind" 65002,
276 "tags": [ 298 "tags": [
277 [ "j", "speech-to-text" ],
278 [ "i", "https://example.com/episode2.mp3", "url" ], 299 [ "i", "https://example.com/episode2.mp3", "url" ],
279 [ "bid", "100", "500" ] 300 [ "bid", "100000" ]
280 ] 301 ]
281} 302}
282``` 303```
283 304
284### `kind:68001`: Job request #3 (summarize both podcasts into one podcast) 305### `kind:65003`: Job request #3 (summarize both podcasts into one paragraph)
285```json 306```json
286{ 307{
287 "id": "125", 308 "id": "125",
309 "kind": 65003,
288 "tags": [ 310 "tags": [
289 [ "j", "summarize" ],
290 [ "param", "length", "1 paragraph" ], 311 [ "param", "length", "1 paragraph" ],
291 [ "i", "123", "job" ], 312 [ "i", "123", "job" ],
292 [ "i", "124", "job" ], 313 [ "i", "124", "job" ],
293 [ "bid", "100", "500" ] 314 [ "bid", "100000" ]
294 ] 315 ]
295} 316}
296``` 317```
@@ -299,29 +320,45 @@ User publishes two job requests at the same time in the order they should be exe
299 320
300This NIP defines some example job types, Customers SHOULD specify these types for maximum compatibility with Service Providers. Other job types MAY be added to this NIP after being observed in the wild. 321This NIP defines some example job types, Customers SHOULD specify these types for maximum compatibility with Service Providers. Other job types MAY be added to this NIP after being observed in the wild.
301 322
302### `speech-to-text` 323### speech-to-text: `kind:65002`
303#### params 324#### params
304| param | req? | description 325| param | req? | description
305|--------------------------------|------|-------- 326|--------------------------------|------|--------
306| `range` | opt | timestamp range (in seconds) of desired text to be transcribed 327| `range` | opt | timestamp range (in seconds) of desired text to be transcribed
307| `alignment` | opt | word, segment, raw : word-level, segment-level or raw outputs 328| `alignment` | opt | word, segment, raw: word-level, segment-level or raw outputs
308 329
309### `summarization` 330### summarization: `kind:65003`
310| param | req? | description 331| param | req? | description
311|--------------------------------|------|-------- 332|--------------------------------|------|--------
312| `length` | opt | desired length 333| `length` | opt | desired length
313 334
314### `translation` -- Translate text to a specific language 335### translation: `kind:65004`
315#### params
316| param | req? | description 336| param | req? | description
317|--------------------------------|------|-------- 337|--------------------------------|------|--------
318| `language` | req | requested language in BCP 47 format. 338| `lang` | req | desired language in BCP 47 format.
319 339
340### image generation: `kind:65005`
341* `input`
320 342
321# Notes 343# Notes
322 344
323* Should there be a possibility of getting the job result delivered encrypted? I don't like it but maybe it should be supported. 345* Job acceptance ambiguity, particularly for job-chaining circumstances is deliberate: service providers could wait until explicit job result acceptance / payment to start working on the next item on the chain, or they could start working as soon as they see a result of the previous job computed.
324
325* Ambiguity on job acceptance, particularly for job-chaining circumstances is deliberately ambiguous: service providers could wait until explicit job result acceptance / payment to start working on the next item on the chain, or they could start working as soon as they see a result of the previous job computed.
326 346
327That's up to each service provider to choose how to behave depending on the circumstances. This gives a higher level of flexibility to service providers (which sophisticated service providers would take anyway). 347That's up to each service provider to choose how to behave depending on the circumstances. This gives a higher level of flexibility to service providers (which sophisticated service providers would take anyway).
348
349# Appendix 3: Service provider discoverability
350
351Service Providers can use NIP-89 announcements to advertise their support for job kinds:
352
353```json
354{
355 "kind": 31990,
356 "pubkey": <pubkey>,
357 "tags": [
358 [ "k", 65002 ], // e.g. speech-to-text
359 [ "t", "bitcoin" ] // e.g. optionally advertises it specializes in bitcoin audio transcription that won't confuse "Drivechains" with "Ridechains"
360 ]
361}
362```
363
364Customers can use NIP-89 to see what service providers their follows use. \ No newline at end of file