aboutsummaryrefslogtreecommitdiffstats
path: root/tools
Commit message (Collapse)AuthorAgeFilesLines
* live: a streamer can delete their own recording (POST /live/<id>/delete, ↵Adam Soltys7 days1-1/+13
| | | | | | | NIP-98), ending the stream if it's on. Its files go, and stream.json stays as 'deleted' so the address answers 410 until the trim takes it. A stream someone reported is taken aside instead (.removed/, kept for reporting), so deleting can't destroy what was reported. Removed and deleted streams no longer count as live (toward MAX_STREAMS, or as exempt from the trim). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* live: WebRTC through Cloudflare's SFU (Realtime) for the app's viewers, a ↵Adam Soltys7 days1-0/+3
| | | | | | | | | | | fraction of a second behind the broadcaster where HLS is 3.5 s. The server only brokers it, and the app's secret stays here ($LIVE_CONFIG sfuAppId/sfuSecret): - POST /live/<id>/rtc/publish (the sender, NIP-98) pushes its tracks. - POST /live/<id>/rtc/watch hands a viewer an offer of them, and /rtc/answer takes its answer. /stats says which publish is current (rtc), and ?watching=1 counts a WebRTC viewer, who never fetches the playlist. HLS carries on as before, for the recording and the other nostr apps. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* live: 1 s HLS segments (keyframes every second), 10 in the live window — a ↵Adam Soltys7 days1-1/+1
| | | | | | | player starts about three segments behind the edge, so most of a viewer's delay was the 2 s segments (8.6 s behind the broadcaster, measured; 4.5 s with these). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* live: a name streams a day after it's claimed (the registrar now records ↵Adam Soltys7 days1-3/+31
| | | | | | | when; older records count from their last update), never if banned; GET /live/allowed says which and until when. Viewers can report a stream (POST /live/<id>/report, NIP-98): kept in .reports.jsonl and DM'd to the admins from the welcome key. An admin takes a stream down (POST /live/<id>/remove {ban}): it ends, its playlists and segments answer 410, its files move to .removed/ (kept for reporting, never trimmed), and a ban stops its sender streaming. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* nwcpush: calls push only to an app that says it rings for them ↵Adam Soltys8 days1-2/+4
| | | | | | | (reasons.call), not to an older one whose worker would show a call as chat. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* nwcpush: calls wake a closed app. Kind 21059 wraps to a registered pubkey ↵Adam Soltys8 days1-0/+87
| | | | | | | are watched; one big enough to be a call offer (an SDP) pushes 'call' at high urgency, a minute's TTL, naming the wrap, which the device fetches from GET /wrap/<id> (kept two minutes; a push can't carry it) and opens with its own key. ICE candidates and hang-ups are too small to wake anyone. A 'call' opt-out like the other reasons. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* live: /live/turn — ICE servers for the app's calls (Cloudflare TURN ↵Adam Soltys8 days1-0/+8
| | | | | | | credentials good for 4 h, to a NIP-98-signed ask; 30 an hour per key, 60 per IP; STUN alone if TURN can't be had). The key comes from $STATE/live/config.json. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* live: live streams sent from the app — the browser's recording over a ↵Adam Soltys8 days1-0/+102
| | | | | | | WebSocket (on 443, through Cloudflare: no ports to open), ffmpeg to 2 s HLS (H.264 720p, AAC), a live window and the whole stream (its recording), viewers counted, a dropped sender resuming the same stream, NIP-98 + coinos-name owners only, 3 at once, 4 h each, recordings kept a week. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Make this the coinosv3-server repo: the services, with coinosv3 pinnedAdam Soltys10 days2-120/+1
| | | | | | | | | | | | | | | | Carved out of coinosv3 (names, feed, nwcpush, blossom, vmedia, bridge and their tests, with history). The shared wallet code they import (src/ark, noffer, bolt12, popular-feed) now comes from a coinosv3 submodule pinned at 082fc2e, the commit the services have actually run on, so moving here changes no behaviour; bumping it is its own commit. The two text helpers translation uses arrived on the server later than that commit and are vendored under names/vendor at the versions deployed, until the pin moves past them. Root package.json pins the npm versions installed on the server. compose.yml runs names, nwcpush and blossom from this checkout (read-only) with configs and state under $STATE, outside the repo, and pins the Bun image. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Translation: into the phone's language, and DeepL with your own key. ↵Adam Soltys2026-10-101-0/+38
| | | | | | | isolabellart saw the Translate button but nothing became Italian: the target was the app's language, coinos has no Italian UI (so it ran in English), and the language guess didn't know Italian. Posts now translate into the reader's choice (Settings → Nostr → Translation, any of 31 languages), else the phone's language when the app has no UI for it, else the app's; the guess knows Italian, Dutch and Polish. And, as Jumble offers: a DeepL API key of your own, kept on the device and sent per request as x-deepl-key — the registrar passes it to DeepL (api-free for ":fx" keys; DeepL refuses browsers) and never stores or logs it; a refused key or used-up quota says so once. names: translateDeepl (prose only, links/tags kept, detected source back, 401/402/422 statuses), CORS allows the header. Deployed to cs names. tools/translate-italian-test.js, tools/deepl-translate.test.js. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
* Translation: a hashtag in a script the reader can't read gets its ↵Adam Soltys2026-10-091-2/+5
| | | | | | | translation in brackets after it — "#総合部絵チャ (…)" — the tag itself kept as the link it is. A post that is only such hashtags now counts as foreign (it was judged by its prose alone, of which it has none, so nothing ever offered to translate it), and ⋯ → Translate on a post with no words says so instead of "already in your language". The registrar's language detection reads the glossed tags too; lang-guess's scriptOf takes a minimum (a two-character tag has a script too). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
* Translation: posts in another language are shown in yours — "Translated ↵Adam Soltys2026-10-091-0/+116
| | | | | | | from Spanish · Show original" under the words, automatic by default for posts the language guess calls foreign (Settings → Nostr → Translation switches it off and leaves a Translate link instead), and ⋯ → Translate for any post. The browser's on-device Translator does it when the language pair is already installed (desktop Chrome); otherwise the registrar's new POST /translate (names/translate.js): a cached, 4-at-a-time front for a LibreTranslate container on cs (names/compose-libretranslate.yml, v1.9.6, 32 languages; none for sw/mr/ta/te, so those readers get no offer). Links, nostr: mentions, :emoji:, #tags and long codes are never sent to the model — only the prose between them, batched in one call (src/translate-split.js, shared). Translations are remembered (coinos-translations, 300). tools/translate-test.js Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
* Drop the third-party HTLC swap bridge tests: the policy they exercise is goneAdam Soltys2026-10-071-190/+0
| | | | | | | | | | 68fd614 removed the VtxoPolicy::Htlc encoding from send.js/proto.js (upstream took type 0x08) and the bridge was retired from the app on 2026-08-02, so htlcLock can no longer build its output (policyTaproot throws on the missing userPubkey). bridge-e2e-test also drove bridge/server.js, which sits on the same dead primitives. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Serve a shared snapshot for the public feedAdam Soltys2026-10-011-0/+50
| | | | Build the default Popular feed once on the server and let browsers verify and paint its signed events before slow media finishes. Keep custom relays, pagination, filtering, and relay fallback on their existing paths.\n\nAdd stale-while-refresh persistence, service-worker bypass, development integration, and unit/browser coverage.
* Accounts from the header, snapshot-first Spending restore, name ownership ↵Adam Soltys2026-09-181-0/+18
| | | | | | | | | | | | | | | | grants. Header buttons always leave the current feature screen; an account with no remembered nostr identity opens Accounts instead of nothing. Logout confirms in place. A scan finishing after logout or an account switch no longer drops the new session into offline mode (stateGeneration guards in sync + ark). Spending restores from the snapshot first. A migration grant names the identity that owns an address; its manager may register it without becoming that identity (names/ownership.js). Browser tests for account access, profile retry, spending restore, thread reply focus, custom-emoji reactions and registry ownership. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Add the trustless Ark->Lightning swap bridgeAdam Soltys2026-07-311-0/+190
A third-party swap provider built on the proposed VtxoPolicy::Htlc: users pay bolt11 invoices from their Ark balance without the ASP being the counterparty and without trusting the bridge with custody. Scope is deliberately one-directional. An ASP's lightning *receive* is already free (0 base / 0 ppm on mainnet); the expensive leg is *send*, with a 20 sat floor that eats 80% of a 25 sat zap. Only that leg is worth undercutting. - bridge/server.js: /info, /quote, /swap, /refund over Bun.serve. Validates the user's HTLC fully (policy, hash, amount, expiry margin, full vtxo chain) before spending anything on Lightning, pays, then claims with the preimage. Crash-safe: state is written atomically and every step re-checks reality (listpays before paying, so a resumed bridge never double-pays). - bridge/ln.js: minimal CLN backend (unix socket or docker exec), named params, maxfee-capped pay, pending/paid introspection. - /refund is safe to expose publicly: the ASP only cosigns a preimage-less HTLC spend when every output pays the refunder, so it cannot be redirected. tools/ark/bridge-e2e-test.js drives the real HTTP API against the patched regtest ASP: a 25 sat zap costs 1 sat, the far-end invoice really settles, the preimage checks out, resubmission is idempotent, an unroutable invoice reports refundable rather than done, and the refund lands every sat back. Co-Authored-By: Claude Fable 5 <[email protected]>