This project is separate from test-environments and test-extended. It has no
public service defaults. Missing configuration fails all checks with a clear
setup error; no check is silently skipped.
Read-only use
Set both endpoints, then run:
npm run test-live-services
| Setting | Purpose |
|---|---|
NOSTRBASE_LIVE_RELAY |
Explicit ws:// or wss:// relay URL. |
NOSTRBASE_LIVE_BLOSSOM |
Explicit HTTP(S) Blossom origin, with no path. |
NOSTRBASE_LIVE_SEARCH |
Optional NIP-50 query; default nostr. |
NOSTRBASE_LIVE_BLOB_SHA256 |
Optional existing public blob to download and verify. |
NOSTRBASE_LIVE_BLOB_FORMAT |
Write fixture: text (default), json, or png; MIME type matches the generated bytes. |
Endpoint credentials, query strings, and fragments are rejected. Redirects are rejected. The report never contains keys, authorization headers, event bodies, or downloaded content. It contains the configured endpoints, declared software and versions, capability decisions, completion status, and counts.
Read-only mode uses a temporary identity for a signed Blossom list request. It sends no relay publications, uploads, or deletions. It makes bounded relay requests, an optional search, pull-only reconciliation, Blossom GET/HEAD/list requests, and browser CORS OPTIONS preflight requests. No private key is needed.
Explicit write tests
To also run scoped write checks, set all these values:
| Setting | Required value |
|---|---|
NOSTRBASE_LIVE_WRITE |
Exactly 1. |
NOSTRBASE_LIVE_TEST_KEY |
Private key of a dedicated test account, as 64 lowercase hex characters. |
NOSTRBASE_LIVE_TEST_PUBKEY |
The matching public key, as 64 lowercase hex characters. |
NOSTRBASE_LIVE_NAMESPACE |
Dedicated namespace starting with nostrbase-integration-; remaining characters are letters, digits, _, or -. |
Supply the test key through your local environment or secret manager. The suite checks the account match and namespace before contacting the deployment. Read-only mode rejects write identity settings to prevent an ambiguous mode.
Six checks keep read completion, declared capabilities, Blossom routes, seeded NIP-50 search, record writes, and blob writes separate. A failed relay check does not prevent an independent Blossom write check.
The record write check creates one unique record, verifies acknowledgement and readback, and requests signed deletion. Declared reconciliation also uses a fresh SDK instance; successful query fallback can recover the record independently of the strict declared-Negentropy check. The blob write check uses a tiny unique fixture, verifies hash and byte identity, lists it, and requests deletion. PNG mode generates a valid one-pixel image with a unique text chunk (158 bytes in the public run). Declared NIP-50 support gets a separate signed kind-1 note with a unique alphanumeric search token and bounded indexing retries; cleanup requests NIP-09 deletion even when publication fails. A relay acknowledgement or deletion request does not prove permanent storage or removal of every remote copy.
Local validation of the same project
node integration/relay/prepare.mjs
node integration/blossom/setup.mjs
NOSTRBASE_LIVE_LOCAL=1 npm run test-live-services
NOSTRBASE_LIVE_LOCAL=1 NOSTRBASE_LIVE_WRITE=1 npm run test-live-services
Local mode starts digest-pinned strfry 1.1.3 and unmodified
hzrd149/blossom-server 6.4.0, commit
32567afb15255c171817a78ed2861cd9e57bf4de. It uses random loopback ports,
temporary storage, a generated test account, and a fresh test namespace. It
rejects external endpoints and external key settings. Docker and the cached,
verified Blossom checkout must be ready. The local run exercises exactly the
same compatibility tests as the external run.
What each result proves
- NIP-01: the relay completes a bounded read and the SDK verifies returned signatures. Empty results are valid.
- NIP-50: a declared search extension must complete a real search request. Without a known seeded result, this proves protocol completion, not search ranking or indexing quality. If the relay does not declare NIP-50, the report states that the extension was not exercised.
- NIP-77:
supported_nips: [77]or the legacynegentropy: 1advertisement counts as declared support. It must complete actual Negentropy negotiation withstrategy: negentropy; silent query fallback fails. Read-only mode can have an empty matching dataset. Opt-in writes also test signed event transfer. An undeclared extension uses an explicit completed query and is reported as unexercised. - Blossom: GET/HEAD routes, SDK error mapping, optional verified blob bytes, authenticated list behavior, and required browser preflight headers. A list route may be absent or deny the temporary identity; the report records that policy result. These HTTP header checks do not replace real browser tests in the separate Blossom service project.
Reports are saved under output/environment/live/compatibility-*.json.
The public driver keeps per-run Vitest results, compatibility reports, and logs
in a separate folder, so it does not replace the main environment suite result.
NOSTRBASE_LIVE_BLOB_FORMAT=png node integration/live/run-public.mjs wss://relay.damus.io https://blossom.band --write
node integration/live/probe-negentropy.mjs wss://relay.damus.io
The driver requires explicit endpoints. It generates a dedicated identity and
namespace, runs read-only checks, then runs writes only with --write. It never
prints the key. Before writes, it journals that generated key with mode 0600
under output/integration-cache/live-identities/, outside the CI artifact paths.
It removes the key only when every attempted resource has a confirmed cleanup.
An absent report or unknown/failed cleanup retains the key for an operator retry.
Safe reports record namespace, record/event IDs, blob hashes, and acknowledgements.
The Negentropy probe uses the exported, unmodified Applesauce negotiation helper and a real WebSocket. It sends an empty-vector pull request, never an event write, and records bounded server notices and negotiation completion. It can distinguish a metadata mismatch from an actual wire rejection.
Public results, 2026-10-04
Public reads and writes were authorized for this run. Checks ran from roughly 09:00 to 09:17 UTC. Public compatibility failures remain failed assertions.
| Service | Observed result |
|---|---|
wss://relay.damus.io |
NIP-11 software strfry 1.1.0-158-gb705403ddf49. Signed reads completed. A record publication, fresh-client query recovery, readback, and signed deletion passed in a complete record check. Some other runs had transient WebSocket/read failures. |
| Damus Negentropy | NIP-11 advertises legacy negentropy: 1, but omits NIP-77 from supported_nips. SDK falls back to a query. Direct modern negotiation receives NOTICE: ERROR: bad msg: negentropy disabled; the strict declared-capability check fails. |
wss://search.nos.today |
NIP-11 software searchnos, version v0.1.0-841b8f6. Actual NIP-50 search completed with verified signed results. General reads fail with error: search filter is required. Synthetic kind-1 publication and deletion both reject with blocked: writes disabled; seeded indexing remains unverified. |
https://blossom.band |
GET/HEAD absence mapping, signed list, and browser preflight passed. Tiny text was rejected with HTTP 400 and a MIME mismatch; JSON was rejected with HTTP 415 and an unsupported-file-type policy. A valid 158-byte PNG upload and signed deletion succeeded. |
| Blossom CDN bytes | The validated PNG descriptor redirects with HTTP 307 from an account subdomain of blossom.band to image.nostr.build. An unauthenticated, bounded redirect diagnostic retrieved exactly the uploaded 158 bytes and verified SHA-256. The SDK download check still fails because the documented SDK policy is redirect: error. No redirect policy was changed. |
Blossom did not declare an application version; server: cloudflare identifies
its HTTP front end. Descriptor diagnostics omit URL query strings and credentials
from their recorded redirect hops. Public tests used only synthetic records,
notes, and fixtures.
An initial Damus cleanup failed after an acknowledged synthetic record write. The first driver kept its account key only in memory, so that cleanup cannot be retried and one synthetic record may remain. Later confirmed deletions are recorded individually. The current driver journals generated keys before writes and retains them when cleanup is not confirmed; it makes no claim of global record erasure.
Known scope of the possible remaining synthetic record:
Relay: wss://relay.damus.io
Timestamp: 2026-10-04T09:01:40.825Z
Namespace: nostrbase-integration-d9d6db45-e7bb-4c68-91fb-2a20f10128c0
Author: 803e83bac266d35f106949450f2f2f8aecd6e9a53b5b598a5b99e8c1c9d35594
Row/event IDs: not retained
Evidence is saved in
output/environment/live/compatibility-1791104500826-73172.json and
output/environment/live/1791104494399-ba41341b-7b6c-43b2-bfee-f6e04f26e76f/summary.json.
Upstream local service logs remain in the usual relay and Blossom output folders.
The local services do not declare NIP-50. Its public request-completion branch was verified against search.nos.today; seeded search was blocked by that relay’s write policy. Hosted routing, certificates, policies, and availability remain specific to the selected deployment. These runs do not prove every public service is compatible.