draft
optional
author:wakoinc
A standardised service definition event allowing services to broadcast services they provide, and client apps to impliment support for a service type that is inter-changable
Nostr is an ecosystem for apps and services built on a common layer. If we can define services using a common definition model, we can allow better user control and choice of services, and reduce implementation costs for adding new services for client apps. This MVP1 is focused on REST/HTTP endpoints.
This will allow
- Client applications to add support for a service type, and users to select interchangable providers
- Service providers to optionally advertise paid services (that may require auth)
- Users to compare and pick the best value provider based on their needs
Note: This NIP has been written to ignore commercials and price information, as it's a separate concern.
- File hosting
- Premium relays
- CDNs
- Translation services
- NIP-05 providers
Nostr Event
{
"kind": 30YYY,
"tags": [],
"content": <services JSON array>,
...other fields
nostrBuild File Upload Service
{
"service_type": "file_upload",
"service_name": "nostrBuild media hosting",
"auth_methods": [],
"endpoint": "https://nostr.build/api/upload/ios.php",
"Inputs": {
"fileToUpload": "file"
},
"headers": {
"accept": "application/json"
},
"validation" : {
"fileToUpload": {
"content_types": ["image/jpeg","image/gif","image/png","image/wepb","video/mp4"],
"max_size_bytes": 26214400,
"accept_multiple": false,
"auth_required": false
}
}
"Response": {
"Success": "@",
"Error": null,
"Result": "@"
}
}
nostrimg File Upload Service
{
"service_type": "file_upload",
"service_name": "nostrimg media hosting",
"auth_methods": [],
"endpoint": "https://nostrimg.com/api/upload",
"inputs": {
"image": "file"
},
"headers": {
"accept": "application/json"
},
"validation" : {
"fileToUpload": {
"content_types": ["image/jpeg","image/gif","image/png","image/wepb"],
"max_size_bytes": 26214400,
"accept_multiple": false,
"auth_required": false
}
}
"Response": {
"Success": "success == `true` && status == `200`",
"Error": "success != `true`",
"Result": "data.link"
}
}
DeepL Free translation Service
{
"service_type": "text-translation",
"service_name": "DeepL Free translation",
"auth_methods": ["API_KEY"],
"auth_required": true,
"endpoint": "https://api-free.deepl.com/v2/translate",
"inputs": {
"text": "text",
"source_lang": "text",
"target_lang": "text",
},
"headers": {
"accept": "application/json",
"DeepL-Auth-Key": "${API_KEY}"
},
"validation" : {
"text": {
"max-length": 2048
},
"source_lang": {
"values": ["$ISO 639-1"],
"transforms": ["uppercase"]
},
"target_lang": {
"values": ["$ISO 639-1"],
"transforms": ["uppercase"]
},
}
"Response": {
"Success": null,
"Error": null,
"Result": "@[0].text"
}
}
DeepL Pro translation Service
{
"service_type": "text-translation",
"service_name": "DeepL Pro translation",
"auth_methods": ["API_KEY"],
"auth_required": true,
"endpoint": "https://api.deepl.com/v2/translate",
"inputs": {
"text": "text",
"source_lang": "text",
"target_lang": "text",
},
"headers": {
"accept": "application/json",
"DeepL-Auth-Key": "${API_KEY}"
},
"validation" : {
"text": {
"max-length": 10240
},
"source_lang": {
"values": ["BG","ZH","CS","DA","NL","EN","ET","FI","FR","DE","EL","HU","ID","IT","JA","KO","LV","LT","NO","PL","PT","RO","RU","SK","SL","ES","SV","TR","UK"],
"transforms": ["uppercase"]
},
"target_lang": {
"values": ["BG","ZH","CS","DA","NL","EN","ET","FI","FR","DE","EL","HU","ID","IT","JA","KO","LV","LT","NO","PL","PT","RO","RU","SK","SL","ES","SV","TR","UK"],
"transforms": ["uppercase"]
},
}
"Response": {
"Success": null,
"Error": null,
"Result": "@[0].text"
}
}
nokyctranslate translation Service
{
"service_type": "text-translation",
"service_name": "nokyctranslate translation",
"auth_methods": ["API_KEY"],
"auth_required": true,
"endpoint": "https://translate.nokyctranslate.com/translate", // POST
"inputs": {
"text": "text", // transform field to "q"
"source_lang": "text", // transform field to "source"
"target_lang": "text", // transform field to "target"
"api_key": "$API_KEY",
},
"headers": {
"accept": "application/json",
},
"validation" : {
"text": {
"max-length": 1024
},
"source": {
"values": ["BG","ZH","CS","DA","NL","EN","ET","FI","FR","DE","EL","HU","ID","IT","JA","KO","LV","LT","NO","PL","PT","RO","RU","SK","SL","ES","SV","TR","UK"],
"transforms": ["uppercase"]
},
"target": {
"values": ["BG","ZH","CS","DA","NL","EN","ET","FI","FR","DE","EL","HU","ID","IT","JA","KO","LV","LT","NO","PL","PT","RO","RU","SK","SL","ES","SV","TR","UK"],
"transforms": ["uppercase"]
},
}
"Response": {
"Success": null,
"Error": null,
"Result": "@.translatedText"
}
}
nostr.wine filter Service
{
"service_type": "relay",
"service_name": "nostr.wine filter service",
"auth_methods": ["RELAY_AUTH"],
"auth_required": true,
"endpoint": "wss://filter.nostr.wine",
"inputs": {},
"headers": {},
"validation": {},
"Response": {}
}
- How to verify/validate the service provider isn't an imposter
- How to link service definitions to paid service offers
- Defined list of auth types
- How to allow AUTH for API keys (need dynamic inputs)
- How to manage service API updates / lifecycle
- We need to formalise the validation approach (syntax + transformations)
- Can/do we need the ability to link status codes to success/fail?
- How best to automate request API_KEY - as we likely don't need human interaction
- Best way to query for services by provider, or by service type
- Best way to query for service offers (membership or paid options)
For response validation and data extraction, we're using JMESPath to help check for success or error, and also extract the response data
"Best way to query for services by provider, or by service type" we can utilize the hashtag NIP that is in place already.