NIPs nostr improvement proposals

NIP-90 - Data Vending Machine

Table of Contents

Data Vending Machine

draft optional

This NIP defines the interaction between customers and Service Providers for performing on-demand computation.

Money in, data out.

Kinds

This NIP reserves the range 5000-7000 for data vending machine use.

Kind Description
5000-5999 Job request kinds
6000-6999 Job result
7000 Job feedback

Job results always use a kind number that is 1000 higher than the job request kind. (e.g. request: kind:5001 gets a result: kind:6001).

Job request types are defined separately.

Rationale

Nostr 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 they don't necessarily care about "who" processes the data.

This NIP is not to be confused with a 1:1 marketplace; instead, it describes a flow where a user announces a desired output, willingness to pay, and service providers compete to fulfill the job requirement in the best way possible.

Actors

There are two actors in the workflow described in this NIP:

Job request (kind:5000-5999)

A request to process data, published by a customer. This event signals that a customer is interested in receiving the result of some kind of compute.

{
"kind": 5xxx, // kind in 5000-5999 range
"content": "",
"tags": [
[ "i", "<data>", "<input-type>", "<relay>", "<marker>" ],
[ "output", "<mime-type>" ],
[ "relays", "wss://..." ],
[ "bid", "<msat-amount>" ],
[ "t", "bitcoin" ]
],
// other fields...
}

All tags are optional.

Encrypted Params

If the user wants to keep the input parameters a secret, they can encrypt the i and param tags with the service provider's 'p' tag and add it to the content field. Add a tag encrypted as tags. Encryption for private tags will use NIP-04 - Encrypted Direct Message encryption, using the user's private and service provider's public key for the shared secret

[
["i", "what is the capital of France? ", "text"],
["param", "model", "LLaMA-2"],
["param", "max_tokens", "512"],
["param", "temperature", "0.5"],
["param", "top-k", "50"],
["param", "top-p", "0.7"],
["param", "frequency_penalty", "1"]
]

This param data will be encrypted and added to the content field and p tag should be present

{
"content": "BE2Y4xvS6HIY7TozIgbEl3sAHkdZoXyLRRkZv4fLPh3R7LtviLKAJM5qpkC7D6VtMbgIt4iNcMpLtpo...",
"tags": [
["p", "04f74530a6ede6b24731b976b8e78fb449ea61f40ff10e3d869a3030c4edc91f"],
["encrypted"]
],
// other fields...
}

Job result (kind:6000-6999)

Service providers publish job results, providing the output of the job result. They should tag the original job request event id as well as the customer's pubkey.

{
"pubkey": "<service-provider pubkey>",
"content": "<payload>",
"kind": 6xxx,
"tags": [
["request", "<job-request>"],
["e", "<job-request-id>", "<relay-hint>"],
["i", "<input-data>"],
["p", "<customer's-pubkey>"],
["amount", "requested-payment-amount", "<optional-bolt11>"]
],
// other fields...
}

Encrypted Output

If the request has encrypted params, then output should be encrypted and placed in content field. If the output is encrypted, then avoid including i tag with input-data as clear text. Add a tag encrypted to mark the output content as encrypted

{
"pubkey": "<service-provider pubkey>",
"content": "<encrypted payload>",
"kind": 6xxx,
"tags": [
["request", "<job-request>"],
["e", "<job-request-id>", "<relay-hint>"],
["p", "<customer's-pubkey>"],
["amount", "requested-payment-amount", "<optional-bolt11>"],
["encrypted"]
],
// other fields...
}

Job feedback

Service providers can give feedback about a job back to the customer.

{
"kind": 7000,
"content": "<empty-or-payload>",
"tags": [
["status", "<status>", "<extra-info>"],
["amount", "requested-payment-amount", "<bolt11>"],
["e", "<job-request-id>", "<relay-hint>"],
["p", "<customer's-pubkey>"],
],
// other fields...
}

Job feedback status

status description
payment-required Service Provider requires payment before continuing.
processing Service Provider is processing the job.
error Service Provider was unable to process the job.
success Service Provider successfully processed the job.
partial Service Provider partially processed the job. The .content might include a sample of the partial results.

Any job feedback event MIGHT include results in the .content field, as described in the Job Result section. This is useful for service providers to provide a sample of the results that have been processed so far.

Protocol Flow

Job feedback (kind:7000) and Job Results (kind:6000-6999) events MAY include an amount tag, this can be interpreted as a suggestion to pay. Service Providers MUST 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.

Customers 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.

Notes about the protocol flow

The flow is deliberately ambiguous, allowing vast flexibility for the interaction between customers and service providers so that service providers can model their behavior based on their own decisions/perceptions of risk.

Some service providers might choose to submit a payment-required as the first reaction before sending a processing or before delivering results, some might choose to serve partial results for the job (e.g. a sample), send a payment-required to deliver the rest of the results, and some service providers might choose to assess likelihood of payment based on an npub's past behavior and thus serve the job results before requesting payment for the best possible UX.

It's not up to this NIP to define how individual vending machines should choose to run their business.

Cancellation

A job request might be canceled by publishing a kind:5 delete request event tagging the job request event.

Appendix 1: Job chaining

A Customer MAY request multiple jobs to be processed as a chain, where the output of a job is the input of another job. (e.g. podcast transcription -> summarization of the transcription). This is done by specifying as input an event id of a different job with the job type.

Service 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 an explicit zap to assume the job was accepted.

This gives a higher level of flexibility to service providers (which sophisticated service providers would take anyway).

Appendix 2: Service provider discoverability

Service Providers MAY use NIP-89 announcements to advertise their support for job kinds:

{
"kind": 31990,
"pubkey": "<pubkey>",
"content": "{
\"name\": \"Translating DVM\",
\"about\": \"I'm a DVM specialized in translating Bitcoin content.\"
}",
"tags": [
["k", "5005"], // e.g. translation
["t", "bitcoin"] // e.g. optionally advertises it specializes in bitcoin audio transcription that won't confuse "Drivechains" with "Ridechains"
],
// other fields...
}

Customers can use NIP-89 to see what service providers their follows use.