Skip to content
NostrGameEnginePublic

Repository files navigation

Nostr Ads Network

Decentralized advertising on top of nostr. draft nip-ADS.

Dependency compatibility

The checked-in Gradle catalog pins the official nostr4j snapshot containing the asynchronous NostrPool.publish API, together with its matching NGE platform API. The stable nostr4j release 0.3.1 has the earlier synchronous publication signature and cannot replace that snapshot. Snapshot artifacts are pinned by timestamp and checked by Gradle dependency verification. Common/JVM and TeaVM platform artifacts share a publication timestamp but have different build numbers, so their catalog versions are kept separately. The JavaScript bindings use the matching official NGE TeaVM compiler fork, pinned in gradle.properties; upstream TeaVM 0.13 cannot compile the platform’s Cleaner-based resource handling.

Keep these dependencies aligned when refreshing the central catalog. Publishing this NostrAds update is required before downstream applications can consume its new binary API compatibility.

Advertiser console

Open /console/ to manage campaigns, search and filter signed ads, inspect their creative and publish revisions. The dashboard distinguishes campaigns returned by the selected relays from copies saved on this device; relay absence alone does not prove that a campaign has stopped everywhere.

Use Log in to choose a browser extension (NIP-07), a remote signer (NIP-46, bunker:// or nostrconnect://), an nsec key, or an encrypted ncryptsec key (NIP-49). Encrypted keys require their password and support scrypt parameters up to a 256 MiB memory limit. The console remembers your account in local storage and restores access when you reopen it. For key login, this stores the signing key, including the unlocked key after an encrypted-key login; passwords are not saved. Remote signers retain their connection key, while browser extensions keep control of the signing key. Log out removes the saved account and signs out other open console tabs. Wallet connections are not saved. Public signed campaigns and account-specific network preferences are saved locally.

Javascript Client Library

This is the client library that you can use to connect a webapp to the network. It uses a subset of the protocol but should be sufficient for the most common use cases.

Building

# Install the exact, audited dependency lockfiles
(cd nostrads-bindings4js && npm ci)
(cd nostrads-js && npm ci)

# Compile the js library
./gradlew --no-daemon :nostrads-js:packJsClient

you should see two new files being created in nostrads-js/nostrads-client.js and nostrads-js/nostrads-worker.js. These together with the optional css nostrads-js/nostr-ads.css, are all you need to include in your webapp.

Initialization

Add this to your pages, make sure to replace <your_path> with the path to the files you just built.

<script src="<your_path>/nostrads-client.js"></script>
<script>
    NostrAds({
        appKey: "<npub>", 
        // relays = [...],
        worker: "<your_path>/nostrads-worker.js", 
        // auth: "<npriv>", 
        // priceSlot: "BTC1_000",
        // category: [],
        // languages: [],
        // advertisersWhitelist: []
        // mimeTypes: [],
        // Exact trusted Blossom/CDN origins allowed to serve image ads:
        allowedImageOrigins: ["https://blossom.primal.net", "https://blossom.band"]
    });
</script>

Image ads are same-origin by default. Add only Blossom/CDN origins you trust to allowedImageOrigins; arbitrary HTTPS image hosts are intentionally rejected to avoid browser-side requests to attacker-selected or internal hosts.

In this snippet:

appKey: "<npub>"

the npub you want to receive payouts for interacting with the ads. This could be a npub you set up for your app, or even the npub of the user if you want the payout to be routed directly to them (eg. as a reward system). In either case, the npub should have an associated kind 0 event with a lud16 or lud06 address to send the payouts to.

relays: [...]

a list of relays to use, leave unset for the default list

worker: "<your_path>/nostrads-worker.js"

is the path to the worker script that you just built.

auth: "<npriv>"

is a private key that will be used to derivate the public key that identify the user in the advertising network. This identity should not be connected in anyway to the real identity (eg. social identity) of the user, and should be rotated often. The purpose of this identity is to allow and advertiser to reach out again to a specific user, eg. to promote a new product they might be interested in, but it should not be used in a way that makes long-term profiling of the user possible. By default leaving this unset will generate a new random private key for the user session, this is generally sufficient.

priceSlot: "BTC..."

the minimum price that the ad must pay to be displayed in the ad spaces. Must be chosen from this list: Price Slot. This filters out low-paying ads. By default it is set to BTC1_000.

category: [...]

a list of category ids to allow in the ad space. Must be chosen from this list: Nostr Ads Taxonomy note: ids are set as strings. Leave unset to allow all categories.

languages: [...]

a list of language codes to allow in the ad space (eg ["en","it"]). Must be chosen from this list: ISO 639-1. Leave unset to allow all languages.

advertisersWhitelist: [...]

list of npubs of advertisers that are allowed to display ads, this can be used to effectively implement adblocking logic. Leave unset to allow all advertisers.

mimeTypes: [...]

a list of mime types to allow in the ad space, Must be chosen from this list: Mime Types. Leave unset to allow all mime types.

Tip

The library runs in a dedicated worker for each page. Private session material is not shared across tabs.

Usage

Once you have the library set up, it will automatically start looking for ad space to fill in the page. To define an ad space you can use this html snippet:

<div class="nostr-ddspace" style="width: 250px; height: 250px;"></div>

Tip

class="nostr-ddspace" is very important, since it is used by the library to identify the ad space. Note: ddspace is not a typo

width: 250px; height: 250px; are valid dimensions for the ad chosen from this list: Ad size and Aspect ratio.

Tip

It is not mandatory to set the dimension in the style like this, the library will use whatever actual display size the element has to find the best ad to fill it, so as long as the space ends up with a size that resembles one of the valid ad sizes, it should work. However, specifying the dimensions manually makes things easier and more predictable.

Configuring the ad space

You can configure the ad space with attributes on the element:

  • appKey
  • priceSlot
  • mimeTypes
  • category
  • languages
  • advertisersWhitelist

They are the same attributes set in the Initialization phase and they will override the values for a specific ad space. The only difference is that arrays are set as comma-separated strings, eg. languages="en,it".

Tip

You can add adspaces dynamically to the page, the library will automatically detect them and fill them with ads.

Delegate Service

If you are an advertiser that wants to self-host, or you want to offer a service to advertisers, you can run a delegate service

The published delegate is a JVM application. Its native safe allocator requires glibc 2.34 or newer; the supported container uses Eclipse Temurin 21 on Ubuntu Noble and runs as an unprivileged user.

docker run -d --name nostrads-delegate \
    --read-only --cap-drop=ALL --security-opt=no-new-privileges \
    --tmpfs /tmp:rw,noexec,nosuid,size=16m \
    -v /srv/nostrads-delegate:/data \
    ghcr.io/nostrgameengine/nostrads/nostrads-delegate:latest

this will automatically generate a new pubkey that you can see with

docker logs nostrads-delegate

Tip

In the snipped above, /srv/nostrads-delegate is the path where all persistent data will be stored, including the private key of the delegate service. Change it to a path that suits your needs.

Important

The container runs as the unprivileged user 10001:10001, so the host directory mounted on /data must be writable by that UID:

mkdir -p /srv/nostrads-delegate
chown -R 10001:10001 /srv/nostrads-delegate

Configuration

The service will start with sane defaults, and no fees. You can configure it by appending arguments to the command line, for example, this will list the available options:

 docker run --name nostrads-delegate \
    ghcr.io/nostrgameengine/nostrads/nostrads-delegate:latest --help

To collect a fee for each successful negotiation, we can use:

docker run -d --name nostrads-delegate \
    -v/srv/nostrads-delegate:/data \
    ghcr.io/nostrgameengine/nostrads/nostrads-delegate:latest \
    --fee 1000:0.02:21000:zap@rblb.it

this will collect a fee of 2% with a minimum of 1 sats and a maximum of 21 sats per negotiation, and will send the fee to zap@rblb.it.

Releases

Sponsor this project

Packages

Contributors

Languages