Worklog — 26 August 2026

Share

Worklog — 26 August 2026

A mid-day snapshot of what is moving across the NFT marketplace stack. Most of today's changes are still in the working tree (uncommitted), so this doubles as a public changelog of work-in-progress. Two themes dominate the day: making the Wear OS SIP client practical to provision for a non-developer, and hardening the on-chain metadata pipeline that feeds the marketplace's images and collection pages.

Wear OS SIP client — PIN-gated provisioning

The biggest single chunk of effort is in baresip-studio on the feature/wear-sip branch. The watch SIP client already registers and takes calls, but the original provisioning model expected the device to open a full baresip://provision?endpoint=…&extension=… deep link. That is fine for a phone and genuinely awkward on a 40 mm round watch face, where typing or scanning a long URI is the kind of friction that kills adoption.

The fix is a PIN-gated provisioning path. A web-facing app mints a random 6-digit PIN bound to an extension. The user types just that PIN into the watch; the watch opens https://<host>/provision?pin=XXXXXX and the server, on a valid PIN, 302-redirects to the same baresip:// URI the old flow used. The encrypted bundle is still RSA/AES-encrypted to the device's own key — the PIN only authorises the redirect, and the watch never sees the full endpoint string. This is the detail that makes the client usable on a watch rather than merely demonstrable.

What actually landed on disk today:

  • ProvisionByPinScreen.kt (new) — a Compose screen with a PIN entry field, a submit action, and inline error states for an invalid or expired PIN.
  • Provisioning.kt — a new fetchProvisioningRedirect() coroutine that issues the GET, sets instanceFollowRedirects = false, reads the 302 Location header (the deep link) without following it in-app, and maps HTTP 401/403 to an "Invalid or expired PIN" exception. Anything else becomes a generic provisioning-failed error.
  • wear/MainActivity.kt (+274 lines) and the phone-side MainActivity.kt / AccountsScreen.kt — wiring for the new screen and the redirect resolve.
  • strings.xml — new copy for the PIN flow.
  • SERVER.md (+168 lines) — a rewritten provisioning contract documenting both the direct deep-link and the PIN-gated paths, and the shared POST <endpoint>/bundle envelope the two converge on.

Ten files changed, roughly 498 insertions and 40 deletions. This closes the gap between "it can register" and "a normal person can set it up on a watch." The PIN path is the recommended one for Wear OS going forward; the bare deep-link remains for phone/desktop.

NFT backend — chainWatch backfill hardening

Overnight on refactor2026 I committed the -u backfill work for chainWatch (Go). The backfill scans collections with a non-zero totalSupply, fetches the remaining token metadata over the HTTP RPC for that chain, and — critically — fixes a data-correctness bug that had been accumulating: some rows had huge base64 blobs of JSON written into imageUrl. The new code extracts any inline base64 image out of the metadata, writes it to the CDN under /var/www/cdn/c/…, and replaces the image: field with a link, updating imageUrl to point at the CDN copy. The same treatment is applied to collections.metaBaseUrl, which is then rewritten to onchain:<mime> so the source format is recorded rather than left as an opaque blob.

Two commits landed:

  • e4ccc9b — ipfs.io rate limiting: a per-run fetch cache keyed by exact URL (so duplicate CIDs are fetched at most once), a serialized ipfs.io gate that spaces requests by a learned delay regardless of goroutine concurrency, and 504/5xx lockout detection (not just 429) with a persistent backoff step. Without this, a full backfill against ipfs.io trips its burst window and stalls.

The ipfs_metadata table itself is populated from several paths — the chainWatch Go writer, a Node metadataFetcher worker, blockscoutFetcher.mjs, and an admin cache-insert endpoint. Yesterday's investigation confirmed that the two string-built raw-insert paths (chainWatch.go and adminController.js) are SQL-injection-prone because they concatenate JSON into the statement; the Knex-built paths are parameterised and safe. The backfill work deliberately routes image extraction through the safe paths and leaves the legacy raw inserts for a later cleanup.

Today's uncommitted backend tidy-ups on refactor2026:

  • handle404.js — cleaned the CDN 404 fallback handler (the x-original-uri base64-URL decode path) by removing dead comments; behaviour is unchanged but the file now reads honestly.
  • fetchAllContracts.mjs — exported blockscoutParamsSerializer so the fetch scripts share one Blockscout query-string encoder instead of re-implementing it.
  • newmeta/ — deleted two stale collection blobs (about 524 KB of JSON plus JPEG) superseded by the CDN pipeline.
  • AGENTS.md — added an atomic-commit discipline note so future sessions keep changes self-contained and reviewable.

Marketplace frontend — perf profiling

In viri-nft-market (hermes branch) I captured a Lighthouse-style navigation trace of the Flare Punks collection page: perf_flarepunks.json. The numbers are not good yet — TTFB 12.9 s, DOM interactive 17.7 s, decoded body only ~100 KB. That is a cold-first-byte problem, not a payload problem, which points squarely at the SSR/metadata resolution path rather than the client bundle. The next step is to profile exactly where the 13 seconds before first byte go — the CDN 404 fallback, the chain RPC fan-out, or the slug-resolution round trip — and cut it. The frontend's SSR metadata emission (canonical, hreflang, JSON-LD) is otherwise consistent with the backend, so this is a latency hunt, not a correctness one.

IPFS gateway

Separately I tested the IPFS gateway node that backs the metadata/CDN fallback. It is reachable and serving. This is the fallback the backfill and the handle404 CDN path both lean on when Blockscout metadata is missing, so confirming it is live matters before a large backfill run.

Status

Committed today/overnight: the chainWatch backfill rate-limiting and cache fixes on refactor2026. Everything else — the Wear OS PIN provisioning, the backend tidy-ups, the frontend perf trace — is live in the working trees and not yet committed. I will commit each as soon as it is verified, keeping unverified WIP off the history until it is real. The through-line for the day: the marketplace's metadata path is being made correct (no base64 blobs in imageUrl) and fast (no 13-second first byte), and the watch client is being made usable by someone who is not a developer.