Commit graph

9 commits

Author SHA1 Message Date
Local Dev
1abaefe948 prometheus: allow BCH-address-only publishers (no BNS name needed)
A BNS name costs cents but is still a gate — someone discovering Prometheus
for the first time shouldn't have to go register sirius.x before they can
publish. Accept a raw cashaddress in the `publisher` field: the sign path
skips the BNS owner-of lookup and treats the field itself as the signing
address, which is still an ECDSA pubkey the chain verifies against.

- listings.js: PUBLISHER_RE = BNS name OR cashaddress; isCashaddr() helper;
  verify branch skips lookupName() and uses the address directly as
  expectedAddress (recovery-is-not-verification still holds — we compare
  the recovered address to the claimed one, both sides)
- pages.js submit form: pattern + placeholder + hint explain both options,
  with the trade-off (address publishers display as truncated hex instead
  of a readable name)
- pages-browse.js: shortenPublisher() collapses raw addresses to
  bchtest:qpumq…sh478 on cards + detail page + <title>; full address kept
  in title attribute for hover, and in the URL path. BNS names are
  untouched.
2026-10-23 02:02:07 +03:00
Local Dev
f5a8ed40e7 prometheus: submit page is a collapsible 4-card wizard
Three polish fixes from a publisher's view in Theseus:

1. The redundant "1 · Upload the binary" h2 under a progress indicator
   that already said "Upload" is gone. The step indicator IS the step
   content now — each step is one <article> card whose head is always
   visible (number + title + state chip) and whose body opens only when
   that step is current.

2. Upload input became a proper drag-and-drop zone. 46 px tall, dashed
   border that lights up acid on dragover (and the whole card wobbles
   1% so there's no doubt the drop will land). Click opens the file
   picker; drop handles files directly.

3. Step headers are clickable. A publisher can jump back to any
   completed step to re-check or edit (canReach() guards: step 4 can't
   be clicked if step 3 hasn't produced a signature yet). The head's
   sub-text updates as each step completes:
     1 · "Upload" → "theseus-setup.exe · 137 MiB · $1 listing fee"
     2 · "Describe" → "My App by foo.x · browsers · v1.0.0"
     3 · "Sign" → "signed · IJIEBM6U…"

Internals:
- One <article data-step="N" data-state="current|complete|pending"> per
  step. CSS keys off data-state for the open/closed body and the chip's
  colour. One setState() helper, one show(n) that walks the four and
  decides each's state based on canReach() + hasStepData(n).
- scroll-to-top replaced by scrollIntoView on the active card, so a
  publisher who jumps back to step 2 after reaching step 4 lands on the
  step-2 header, not the hero.
- acceptSignature(base64, how) is now one path used by both the Theseus
  wallet handler and the paste listener — stops the two from drifting
  on what counts as "signature captured".
2026-10-23 01:17:54 +03:00
Local Dev
8b76f16c1e prometheus: size-tiered pricing, 2 GiB cap, new hero copy, SEO LD
Three threads, one commit:

1. Size-tiered listing fee instead of a flat $1

   priceUsdForSize(bytes) in service/payments.js:
     0 - 100 MiB         → free
     100.001 - 200 MiB   → $1
     200.001 - 300 MiB   → $2
     ...
     1900.001 - 2000 MiB → $19
     2000.001 - 2048 MiB → $20 (ceiling at 2 GiB)

   listingNeedsPayment(bytes) replaces paymentsRequired() — a free-tier
   upload always publishes immediately even when PAYMENT_REQUIRED=1; the
   operator switch only governs the paid tiers. createInvoice derives the
   fee from the listing's own size at invoice time.

2. Upload cap back to 2 GiB

   The 200-MiB cap from the previous commit (which fixed the Caddy-413
   HTML bug) is reverted. The paywall is now the real size gate; Caddy
   is just the ceiling. p2pexchange Caddyfile already updated + reloaded.

3. Hero + SEO

   Hero leads with the plain-English product category:
     The decentralized app store.
     A home for apps, programs, dApps, and libraries — listed and settled
     on the Bitcoin Cash chain, hosted on the decentralized web, endorsed
     by signature. Anyone can publish. Nobody can gatekeep.

     Named for the one who gave fire to humans and did not ask the gods first.

   "hosted on Sia" → "hosted on the decentralized web". Sia is the
   mechanism, not the pitch — general readers aren't tripped on the
   acronym on the hero.

   Meta description rewritten to lead with "decentralized app store" and
   name the long-tail terms the audience searches for ("web3 browsers",
   "BCH wallets", "mobile and desktop apps").

   Two JSON-LD blocks (WebSite + Organization) inlined on home via
   layout's headExtra channel — knowledge panel + WebSite result card.

4. Submit wizard

   Step 1 result shows the computed listing fee under the scan verdict,
   so publishers see the price BEFORE filling out the manifest.

   Step 4 publish branches on the server's payment_required flag. On a
   paid listing it POSTs /api/invoice right after the listing POST and
   renders a payment card with the exact sat amount, the operator receive
   address, a `bitcoincash:` URI the wallet opens, and the invoice status
   URL for polling.
2026-10-22 23:31:43 +03:00
Local Dev
bbef07c825 prometheus: 200 MiB upload cap + Caddy block fix for the "<" error
Two bugs under one roof.

The visible one: a publisher uploading anything over 10 MB on the live
site got "Network error — Unexpected token '<'" in the browser console.
Cause: the Caddy block I wrote for prometheus.x had
`request_body { max_size 2GB }` nested inside the handle block, where it
is a syntax Caddy parses but does not apply, so Caddy's own 10 MB
default was still the gate. Any oversize POST got Caddy's HTML 413 page,
which the browser's JSON parser choked on at the leading `<`.

Fix: moved `request_body { max_size … }` to the top of the prometheus.x
site block (outside `handle`), where Caddy actually honours it. The live
Caddyfile on p2pexchange is already patched + reloaded; a 50 MB upload
verified through the end-to-end path (Caddy → service → clamd → signed
scan report).

The scope one: 2 GiB was more than Prometheus should offer to publishers
at launch. Mobile apps are usually ~100 MB and even most desktop
installers come in well under 200 MiB. Dropping to 200 MiB:

  - Fits every Android APK and most desktop installers.
  - Shrinks the operator's Sia storage bill (fewer accidental Steam-size
    uploads).
  - Keeps the "publishers of larger binaries self-host and submit a
    manifest with the external URL + SHA" escape hatch — the scanner
    still fetches the URL and checks the hash, so a 5 GB game isn't
    locked out, Prometheus just doesn't eat its storage.

Changes:
  service/uploads.js       MAX_FILE_BYTES default: 2 GiB → 200 MiB
  service/pages.js         /submit step-1 subtitle: "any size up to
                           2 GiB" → "up to 200 MiB"
  deploy/prometheus-x.conf (unused angie fallback) 2g → 200m
  deploy/apps-silentmode-st.conf (unused angie fallback) 2g → 200m

PROMETHEUS_MAX_UPLOAD_BYTES env still overrides per-instance.
2026-10-22 23:20:19 +03:00
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
a9ca956c2e prometheus: standalone identity + first-wave Silent Mode listings
Four changes, one coherent pass:

1. Prometheus gets its own flame mark (SVG, not an emoji). Used in the
   topnav brand, in the hero at 88 px, and as the favicon. Replaces the
   🔥 glyph in the service and in site/prometheus/index.html.

2. silentmode.st/prometheus is now a brief introduction with two big
   buttons (Open prometheus.x · Open apps.silentmode.st mirror) and a
   short "what / who / status" block, instead of the full category
   grid that duplicated what prometheus.x already renders. The /prometheus
   page's job on silentmode.st is "point you at the real site," not host
   the content.

3. Footer on the Prometheus service drops the "Silent Mode — a Deviant
   project" byline and reframes the sibling-x links as "Also live in the
   decentralized web:" so Prometheus reads as its own site rather than a
   Silent Mode subpage. Keeps the attribution via the MPL-2.0 + source
   link on the final line. aegis.x and pithos.x added to the sibling row.

4. Four Silent Mode apps seeded as first-wave listings
   (scripts/seed-listings.json + scripts/seed.mjs). Theseus Navigator,
   Ariadne's Thread (Windows + Android), Pithos. Each carries the
   project's own SVG icon from public/icons/, pulled from the project's
   own site. The seed is a direct-DB insert (not through /api/listing)
   with signer_address = chipnet operator and signature = sentinel
   `SM_OPERATOR_SEED`; scan_report is an operator-attested envelope
   signed by the service key so the display pipeline is unchanged. Run
   with `node scripts/seed.mjs`; idempotent (skips existing (publisher,
   slug)).

   Deliberately NOT seeded: Theseus extensions that have no standalone
   form — pdf-editor, docx-editor, shield-blocker, bergamot provider.
   They ship via Theseus's own OTA, not as Prometheus listings. Aegis
   is in the same boat today (a Theseus bundled add-on without a
   standalone binary); its icon is included in public/icons/ for when
   it has its own distribution.

Real file sizes + SHAs come from the current dl.silentmode.st manifest
(theseus 0.3.89, ariadne resolver 0.1.13, ariadne android 0.22,
pithos-desktop 0.3.26). When APS1 lands cross-session, these listings
migrate to real on-chain records signed by each project's own publisher
key and the sentinel values go away.
2026-10-09 01:23:21 +02:00
Local Dev
bb07c73aaf prometheus: browse pages — categories and per-listing detail
/c/<slug>  renders one of the eight categories with every active listing
           in it, newest first, as a grid of cards. Empty category shows
           a "be the first" prompt rather than a dead page.

/app/<pub>/<slug>  renders a listing: icon + name + publisher + version
                   chip, a prominent Download card with size, hash chip,
                   signed scan badge (green/clean | red/infected |
                   neutral/unknown), description, homepage link, who
                   signed, and an endorsements placeholder for when APS1
                   lands.

The home-page and /browse grids now read real counts from the DB — the
hardcoded "0 listings" placeholders are gone. A new helper
`renderCategoriesGrid()` is the one place category cards are rendered,
so the two surfaces always agree.

404s for unknown categories (`/c/nope`) and unknown listings
(`/app/foo/bar`) go through the same notFound page and now actually
return HTTP 404 instead of 200 — pages opt in by returning the
notFound object, server detects it by title and sets the status.

CATEGORIES is the single source of truth for slug/icon/title/blurb;
adding a new category now means one row here plus one entry in the
listings allow-list.

Verified end-to-end with three listings across three categories: counts
update, cards render, detail page shows the signed scan badge, 404s
return 404.
2026-10-08 01:56:52 +02:00
Local Dev
c4566b4ea3 prometheus: upload + ClamAV scan + signed scan reports
POST /api/upload accepts a multipart file, spools it to a temp path while
hashing to SHA-256, hands the on-disk copy to clamd via INSTREAM, keeps
the file at uploads/<sha256>.bin on a clean verdict and discards it on
anything else. The response carries the (hash, size, verdict) tuple
signed by the service's ed25519 key — the artifact the manifest step
will attach to the APS1 record later.

For local dev on a box without ClamAV, MOCK_SCAN=clean|infected|error
short-circuits the TCP call while still draining the bytes end-to-end.
CLAMD_HOST / CLAMD_PORT / PROMETHEUS_MAX_UPLOAD_BYTES override the
defaults (127.0.0.1:3310, 2 GiB).

Service key lives at .keys/service.key (gitignored, 0600), generated on
first start if absent. The matching public key is served at
/.well-known/prometheus-service-key.json so clients can cache it per
origin. ed25519 chosen over BIP-137 because the scan report is a service
attestation, not a Bitcoin thing — the key never signs value, never
signs for a user, never touches the chain.

Canonicalisation walks the payload recursively (sorted keys at every
level, no whitespace). The first draft used JSON.stringify with a keys
array, which only serialises top-level keys from the array — nested
objects came out as {}, so the signature verified nothing useful.

/healthz pings clamd (or the mock) so monitors can tell when the scanner
is down.

The /submit page gets a working upload form that drives the endpoint
and renders the full signed response, so the whole chain is testable in
the browser as well as via curl.

One dependency added: busboy. Rolling our own multipart parser was the
obvious alternative; busboy is one small, maintained library with no
runtime deps of its own, and multipart RFC 7578 edge cases are not
places this project earns anything by rediscovering.
2026-10-05 22:41:55 +02:00
Local Dev
2c5df40d77 prometheus: scaffold the standalone service — pages, routing, static assets
New top-level Prometheus/ directory holds the Node service that will own
prometheus.x once the on-chain record flips from the silentmode.st
redirect to ip+tls. Nothing dynamic yet — this milestone delivers the
site skeleton and proves the family's visual conventions port cleanly to
a dynamic server.

Shape:
  Prometheus/service/server.js    node:http listener + map-based router
  Prometheus/service/layout.js    document shell, topnav, footer
  Prometheus/service/pages.js     home / browse / submit / publish / boundaries
  Prometheus/public/style.css     ported silentmode.st tokens + category grid
  Prometheus/public/favicon.svg   flame mark

Routes: /, /browse, /submit, /publish, /boundaries, /static/*.
Dynamic routes (/api/upload, /api/manifest, /api/publish, /api/pay,
/admin/*) land here in later milestones per the architecture doc.

Launch config .claude/launch.json gets a prometheus-service entry on
port 3700 so the preview harness can run it.

The pre-pivot WordPress-based scaffold at site-prometheus-x/ is left
in place for the next session to delete deliberately — the architecture
doc marks it as abandoned for prometheus.x but keeps the Sirius Press
infra fixes it carried in.
2026-10-05 22:22:47 +02:00