This service could be used by any nostr application to allow users to upload and serve files (including large files) from a fast content delivery network.
Satellite CDN is a paid service. Storage is "pay as you go" at a flat rate of USD $0.05 / GB / month, payable in sats via lightning at the current USD/BTC exchange rate. Data transfer is free and unlimited. This pricing structure is a good fit for social applications because users will not be charged extra if their content becomes popular. For example, if a nostr user uploads a 1 GB podcast to the CDN which goes viral and gets downloaded a million times, the user will still only pay a predictable rate of USD $0.05 per month.
For each file added to the CDN, Satellite computes the relevant NIP-94 parameters and returns them to the client upon completion of the upload. The client may then verify these values and/or use them to create a kind 1063
event.
Let's go through a simple example demonstrating how to buy credit, fetch the user's account data, and upload a file using the API.
// Prompt user to sign a kind 22242 auth event
// requesting to buy 1 GB month of storage
const requestCredit = await window.nostr.signEvent({
created_at: Math.ceil(Date.now() / 1000),
content: 'Request Storage',
kind: 22242,
tags: [
[ 'gb_months', '1' ]
]
});
// Send a GET request to the API with the signed event
// as the uri encoded auth param. The API responds with
// a json object which includes an offer to purchase the
// requested storage and expected terms of payment.
const service = await fetch(`https://api.satellite.earth/v1/media/account/credit?auth=${encodeURIComponent(JSON.stringify(requestCredit))}`);
// Prompt user to sign the returned payment event
const payment = await window.nostr.signEvent(service.payment);
// Use another GET request to send the signed payment event
// to the returned callback url to obtain a lightning invoice
const invoice = await fetch(service.callback + `?amount=${service.amount}&nostr=${encodeURIComponent(JSON.stringify(payment))}`);
// Prompt user to pay the lightning invoice...
The user's account will be credited as soon as Satellite detects that the invoice was paid (usually within a few seconds)
// Prompt user to sign auth event requesting account info
const requestAccount = await window.nostr.signEvent({
created_at: Math.ceil(Date.now() / 1000),
content: 'Authenticate User',
kind: 22242,
tags: []
});
// Fetch user's account info
const account = await fetch(`https://api.satellite.earth/v1/media/account?auth=${encodeURIComponent(JSON.stringify(requestAccount))}`);
// Check if storage was allocated
if (account.creditTotal > 0) {
alert('payment succeeded');
}
Note that the account object contains much more information than just creditTotal
(including information about specific payments). See the API section below for a comprehensive explanation.
// Prompt user to sign auth event for file upload.
// Note that all tags are optional (see API section)
const uploadAuth = await window.nostr.signEvent({
created_at: Math.ceil(Date.now() / 1000),
kind: 22242,
content: 'Authorize Upload',
tags: [
[ 'name', file.name ],
[ 'size', 1234567 ],
[ 'label', 'foo' ]
]
});
// Prompt user to select a file to upload and
// send it as the body of a PUT request
const response = await fetch(`https://api.satellite.earth/v1/media/item?auth=${encodeURIComponent(JSON.stringify(uploadAuth))}`, {
method: 'PUT',
body: file
});
// {
// "created": 1685997838,
// "sha256": "60bb967f08cb8721def12810243673bcb4b046e0733ec901be2e3ffd904ed274",
// "name": "Space Odyssey Theme.mp4",
// "url": "https://cdn.satellite.earth/60bb967f08cb8721def12810243673bcb4b046e0733ec901be2e3ffd904ed274.mp4",
// "infohash": "a233527c1dc9380f8aec6c23b0db4044c75ae39a",
// "magnet": "magnet:?xt=urn:btih:a233527c1dc9380f8aec6c23b0db4044c75ae39a&dn=Space+Odyssey+Theme.mp4&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com",
// "size": 4842030,
// "type": "video/mp4",
// "nip94": [
// [ "x", "60bb967f08cb8721def12810243673bcb4b046e0733ec901be2e3ffd904ed274" ],
// [ "m", "video/mp4" ],
// [ "i", "a233527c1dc9380f8aec6c23b0db4044c75ae39a" ],
// [ "url", "https://cdn.satellite.earth/60bb967f08cb8721def12810243673bcb4b046e0733ec901be2e3ffd904ed274.mp4" ],
// [ "name", "Space Odyssey Theme.mp4" ],
// [ "size", "4842030" ],
// [ "magnet", "magnet:?xt=urn:btih:a233527c1dc9380f8aec6c23b0db4044c75ae39a&dn=Space+Odyssey+Theme.mp4&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com" ]
// ]
// }
When the upload completes successfully, the API returns a reponse with the file metadata, including tags that the client can use to create an NIP-94 event.
All routes listed below are relative to the endpoint https://api.satellite.earth/v1/media
.
Note that for all requests that require an auth
param, the request may be fail with a 403
response if the created_at
field of the kind 22242
event deviates too far from the time of the request according to the server.
auth
- The signed, stringified, url-encoded kind22242
event to authorize a file upload. The value of thecontent
field MUST be equal toAuthorize Upload
. Clients MAY include any or all of the following tags in the signed auth event: aname
tag to indicate the file's name, asize
tag (which, if present, will cause the request to fail if the actual size of the file does not match this value) and alabel
tag (the value being an arbitrary string which applications might need to otherwise classify/identify the uploaded file). Note that a unique auth event must be signed per request. Reusing an auth event will fail.
200
- File uploaded successfully
created
Number
- Time of upload, according to serversha256
String
- SHA256 hash as computed by servername
String
- Filename as provided by clienturl
String
- URL to access file on CDNinfohash
String
- Torrent infohash as computed by servermagnet
String
- Torrent magnet linksize
Number
- File size in bytestype
String
- File MIME type as inferred by servernip94
Array
- Tags for creating NIP-94 event, if that's what the client wants to dolabel
String
- Value from label tag in auth event, if provided
400
- Bad request
402
- Payment required (ensure that account has credit)
403
- Auth missing, failed to verify, or not unique
The body of the request MUST contain the file's data
auth
- The signed, stringified, url-encoded kind22242
event to authorize a file deletion. Clients MUST specify thesha256
hash of the file they wish to delete as a tag in this event. The value of thecontent
field MUST be equal toDelete Item
.
200
- File deleted successfully
400
- Bad request (probably missing sha256
tag)
403
- Auth missing or failed to verify
auth
- The signed, stringified, url-encoded kind22242
event to authorize fetching account info. The value of thecontent
field MUST be equal toAuthenticate User
.
200
- Fetched account
storageTotal
Number
- Total bytes currently storedcreditTotal
Number
- Total number of GB months purchased to dateusageTotal
Number
- Total number of GB months used to datepaidThrough
Number
- Timestamp at which storage will expiretimeRemaining
Number
- Seconds remaining until storage expires, from time of requestrateFiat
Object
- Cost per GB per month in various fiat currenciesusd
Number
- Rate per GB per month in US dollars
exchangeFiat
Object
- Exchange rate between sats and various fiat currencies at time of requestusd
Number
- Exchange rate beteen sats and US dollars at time of request
files
Array
- Metadata for currently stored filesFile
Object
transactions
Array
- Record of paid invoicesTransaction
Object
order
Object
- Kind9733
event representing Satellite's offer to purchase CDN servicepayment
Object
- Kind9734
event representing user's payment for servicereceipt
Object
- Kind9735
event confirming purchase of service
403
- Auth missing or failed to verify
auth
- The signed, stringified, url-encoded kind22242
event to request an offer of services on behalf of the user. The event MUST include agb_months
tag specifying an integer number (formatted as a string) of GB months credit the user wants to purchase. The value of thecontent
field MUST be equal toRequest Storage
.
200
- Fetched account
callback
String
- The URL to send to signed offer toamount
Number
- The amount in millisats requried for payment of requested servicerateFiat
Object
- Cost per GB per month in various fiat currenciesusd
Number
- Rate per GB per month in US dollars
offer
Object
- Kind9733
event representing Satellite's offer to purchase CDN service
403
- Auth missing or failed to verify