Commit graph

2 commits

Author SHA1 Message Date
Local Dev
916440fb31 prometheus: /submit becomes a 4-step publisher wizard
The submit page was a one-form upload tester that said "not wired yet"
on the three steps that would turn an upload into a published listing.
It now walks a publisher through the whole thing in-page.

Four client-side steps, state in one object (W = {file, upload, manifest,
message, signature}), scroll-to-top on each transition, back buttons
preserved so revisions are a correction not a restart:

  Step 1 — Upload
    File picker → POST /api/upload. Response rendered as a card showing
    name + size + short hash + the scan verdict chip ("scan ✓ clean
    (clamav, 2ms)"). Advance button only enabled when scan is clean;
    infected or error paths show the reason inline and stay on step 1.

  Step 2 — Describe
    Form fields for publisher BNS name, slug, display name, category
    (optgroup'd by top-level: Apps subs, then Games, then FinTech),
    version, description, download URL, homepage + icon (optional).
    Pattern + maxlength validation matches the server's rules in
    service/listings.js. Submit builds W.manifest and canonicalises
    the signing message.

  Step 3 — Sign
    Shows the canonical message to sign. If window.bitcoincash is
    injected (Theseus with Aegis), a one-click "Sign with Theseus
    wallet" button calls bitcoincash.signMessage(). Fallback paste
    textarea accepts a 86–88 char BIP-137 signature from any wallet
    (Electron Cash, Deviant, Cashonize). Both paths populate
    W.signature and enable the continue button.

  Step 4 — Publish
    POST /api/listing with the full body. On 201 shows the listing
    card + a "View your listing →" button pointing at
    /app/<pub>/<slug>. On error shows the server's reason inline and
    re-enables the Publish button for retry.

Canonical JSON in the client is a direct copy of service/listings.js's
canonicalJson — same bytes, same signature. A test that drifts them
apart would be nice; for now a comment on each copy flags the pairing.

scripts/test-listing-roundtrip.mjs's default category changed from the
old `utilities` slug (removed in the taxonomy change) to `productivity`.
2026-10-22 22:58:35 +03:00
Local Dev
72f7d41902 prometheus: signed listings — BIP-137 publisher sign, SQLite index
POST /api/listing accepts a JSON body with the manifest fields, a
reference to a signed scan report from /api/upload, and a BIP-137
signature. Before the row lands in SQLite the service verifies three
things:

  1. the attached scan_report was signed by this service, is for the
     exact (hash, size) the listing claims, and the verdict is clean —
     a publisher cannot swap in a different file's clean report;

  2. the publisher's signature recovers to a valid cashaddress, and
     that address matches the on-chain owner of the publisher's BNS
     name (gateway /api/name/<name>) — recovery alone is never enough
     per [[recovery-is-not-verification]];

  3. the usual shape checks: category in the allow-list, sha256 is 64
     hex, download URL is http(s), slug matches [a-z0-9-]{1..64}, body
     under 1 MiB.

GET /api/listings and GET /api/listings/<publisher>/<slug> are read-only
endpoints the browse milestone will render from.

MOCK_BNS_OWNER=<cashaddr> short-circuits the gateway call for local
testing without a chipnet gateway reachable — same pattern as MOCK_SCAN.

Dependencies:
- @bitauth/libauth, for the secp256k1 primitives behind BIP-137 recovery
  and the cashaddress encoder. Same primitive Argus's registrar uses, so
  signatures from Argus-compatible wallets verify unchanged.
- node:sqlite (ships with Node 22+) for the listings DB — one file next
  to the service, WAL journaling, no native build step. engines bumped
  to ">=22".

Lifted service/lib/sign-message.js from Argus so Prometheus does not
have to pull the resolver's whole dependency tree in for one verifier.
Removed its NETWORK import; the default address prefix comes from
BNS_NETWORK=chipnet|mainnet instead.

INTERIM: no APS1 record on chain yet. The signed row IS the listing;
when APS1 is defined cross-session the service will add an on-chain
publish step that points at the same signed manifest. The browse and
admin milestones can proceed against this DB in the meantime.

scripts/test-listing-roundtrip.mjs drives the whole flow end-to-end
with a deterministic test key, so a sanity check is a two-command
sequence: start the server with MOCK_SCAN=clean + MOCK_BNS_OWNER=<the
test address>, then run the script. Negative paths (fake scan report,
bad category, duplicate slug) also verified by hand.
2026-10-08 01:48:12 +02:00