Commit Graph
432 Commits
Author SHA1 Message Date
thePR0M3TH3ANandClaude Opus 4.8 861407cbf6 Editor: publish under your own identity (NIP-07 / nsec) with PIN-encrypted nsec
Let authors publish a flyer under a stable, identifiable key when they want
one, while keeping anonymous-by-default. The editor now signs under one of
three identities, routed through activeIdentity()/signActiveEvent():

- Anonymous (default, unchanged): the per-device key, signed locally.
- NIP-07 extension: signs via window.nostr.signEvent; the secret never
  enters the page.
- Imported nsec: decoded to a key used to sign. By default the secret lives
  only in memory for the session. "Remember on this device" stores it
  encrypted with a PIN (PBKDF2 SHA-256, 600k iterations, random salt ->
  AES-GCM-256); the plaintext secret is never written to storage. On reload a
  remembered key is locked until the PIN is entered; a session-only key falls
  back to anonymous.

The signer pill, author npub, naddr, and poster URL all follow the active
identity. New identity controls are localized across all 14 languages.

Security note (also in docs): a short numeric PIN is brute-forceable offline
if the encrypted blob leaks, so it guards casual snooping, not a targeted
attacker — the NIP-07 path is stronger, and NIP-46 is a documented follow-up.
The minimum PIN is 4 digits but longer is allowed and encouraged.

Also updates README, the client spec (new "Publishing identity" section),
the roadmap (#1/#2 marked in progress with phases shipped), and the e2e
README. Covered by the cross-engine Playwright suite (default-anonymous,
NIP-07 connect + publish, session-only nsec without persistence, invalid
nsec, PIN encrypt -> lock on reload -> wrong/right PIN -> unlock, and forget).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-10 10:16:53 -04:00
thePR0M3TH3ANandClaude Opus 4.8 1297a73332 Bulletin board: login gate, web-of-trust filter, and key connect methods
Gate the board behind a connected Nostr identity and filter what it shows
to the viewer's web of trust, so it is no longer an unmoderated firehose.

- Login gate: the board shows a Connect prompt instead of the table until a
  Nostr identity is connected; the connected npub is surfaced with a
  Disconnect control and remembered (pubkey only) for silent reconnect.
- Connect methods: NIP-07 extension, pasting an nsec (decoded in-page to a
  pubkey, then discarded — the board reads, never signs), or creating a new
  key (reveals the nsec to save, reuses the device key if present).
- Web-of-trust filter (NIP-02): defaults to flyers from authors in the
  viewer's follow graph, with a "Show all" opt-out. A fresh key with no
  follows is seeded from a pinned curator account so new viewers still get a
  curated board; if no trust data loads at all, it shows everything.
- Localized across all 14 languages (gate, filter, and connect strings),
  RTL included.

Covered by the cross-engine Playwright suite (gate shown/hidden, connect via
each method, pubkey-only persistence, web-of-trust filtering + show-all, and
the curator-seeded fallback).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-10 10:16:24 -04:00
thePR0M3TH3ANandClaude Opus 4.8 b92cb018c5 Roadmap: plan identity + web-of-trust gating for the bulletin board
Mark board moderation (#1) and authenticity (#2) as Planned and add a joint
"identity + web-of-trust on the board" direction:

- Login enables a trust-filtered view, not access control (you cannot gate
  writes to public relays; you filter what the board displays).
- Keep anonymous creation + printing available; require identity only to
  appear on the public board.
- Web-of-trust (NIP-02 follow graph) is the real spam defense, since keys are
  free; login alone is just a soft deterrent.
- Support NIP-07 plus nsec import / NIP-46 / promoted anon key so mobile (the
  scanned-QR audience) isn't locked out.
- Phased plan (identity → WoT filter → blocklist/report/NIP-05) and the two
  open decisions: gate viewing or not, and which login methods at launch.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-09 22:25:38 -04:00
thePR0M3TH3ANandClaude Opus 4.8 c8c715f438 Document known issues and roadmap
Add docs/roadmap.md capturing product/threat-model gaps that are not code
bugs, in priority order: (1) bulletin board has no moderation/abuse story,
(2) anonymous keys give no authenticity, (3) client distribution is
centralized on one domain, (4) relay permanence is overstated, (5) minimal
content-safety guardrails, plus smaller items. Each entry notes the problem,
current mitigation, why it matters, and possible directions.

Link it from the README and the spec's bulletin board section.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-09 22:21:34 -04:00
thePR0M3TH3ANandClaude Opus 4.8 e0ea31eaef Widen paper-size selector and balance bulletin-board link spacing
- Give the paper-size selects a min/max width so "US Letter (8.5 × 11 in)"
  is readable instead of truncated.
- Add equal spacing below the bulletin-board link to match the gap above it.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-09 22:15:03 -04:00
thePR0M3TH3ANandClaude Opus 4.8 7ab7e412ec Show a publish confirmation modal with the flyer URL
After Sign and publish succeeds, open a modal that tells the author the
flyer is published and shows its URL (pre-selected) with Copy and Close
buttons, so they know the process completed and where to view/share it.

- New modal markup in index.html + overlay/box styles (hidden in print).
- Closes on the Close button, backdrop click, or Escape; Copy writes the URL
  to the clipboard with a "Copied" confirmation.
- Localized for all 14 languages (published_title / published_message /
  close); the URL is result.posterUrl.
- Playwright: add a test that stubs the relay socket, publishes, and asserts
  the modal shows the localized text and the naddr poster URL. 84 tests pass
  across all engines.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-09 22:06:30 -04:00
thePR0M3TH3ANandClaude Opus 4.8 0044b4b797 Fix unstyled bulletin board on Vercel (cleanUrls/trailingSlash)
vercel.json sets cleanUrls + trailingSlash, so board.html is served at
/board/. Its relative asset paths (nostr-client.css, board.js, locales.js,
vendor/...) then resolved to /board/... and 404'd, leaving the page as raw
unstyled HTML stuck on "Loading…". Use root-absolute paths instead, and
point the back link at "/". Add an e2e guard asserting board.html assets are
root-absolute (the dev server can't reproduce Vercel's URL rewrites).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-09 21:38:26 -04:00
thePR0M3TH3ANandClaude Opus 4.8 98086a0ad4 Add bulletin board page listing recent flyers from the relays
A small screen-only link below the flyer preview (outside the printable
sheet, hidden in print) opens board.html: a spreadsheet-style list of the
most recent VoxVera flyers found on the default relays.

- Columns: title, link (the poster URL shown as "link"), posting npub,
  language, and posted date; every column is sortable (default date
  descending). Replaceable events are de-duplicated per author+d identifier,
  keeping the latest.
- board.js is independent of nostr-client.js (which exports nothing): its UI
  strings live in BOARD_UI for all 14 languages, and it reuses the vendored
  nostr-tools and locales.js (language names + RTL direction).
- The board has its own language selector that reads/writes the same
  voxvera_nostr_lang localStorage key as the main client, so the choice
  carries across pages; switching it re-localizes the chrome, headers,
  language labels, and date formatting and flips RTL.
- Untrusted relay content is escaped, and link URLs are rejected unless
  http(s) so javascript:/data: never render as a link.

Docs updated (README, spec, AGENTS). Playwright: add tests for the
out-of-sheet print-hidden link, the localized sortable columns, and the
board language selector (incl. RTL + persistence). 72 tests pass across
Chromium, Firefox, WebKit, and Android/iOS mobile emulations.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-09 16:28:32 -04:00
thePR0M3TH3ANandClaude Opus 4.8 127f9c3134 Locale-aware defaults: browser language list + US Letter/A4 paper size
Language inference:
- Walk navigator.languages (the full ordered preference list), not just
  navigator.language, and pick the first base language we support. A user who
  prefers e.g. Dutch then German now gets German instead of the English
  fallback. De-duplicates the default-language resolution into setDefaultText.

Paper size (US Letter + A4):
- Add A4 (210x297mm) alongside US Letter (8.5x11in). Sheet dimensions move to
  CSS custom properties (--sheet-w/--sheet-h) toggled by a paper-letter/paper-a4
  class on <html>; the tear-off column and QR codes stay fixed physical sizes
  and the content column flexes.
- @page size is injected by JS (id voxvera-page-size) since @page cannot read
  CSS variables across engines; the print block keeps margin:0 and a Letter
  default for the pre-JS state.
- Default paper is chosen from the browser locale's region subtag
  (en-US -> Letter, en-GB/de-DE -> A4) — "by location" without geolocation,
  falling back to Letter when no region is present. A manual override selector
  (topbar + viewer controls) persists in localStorage and wins over the default.
- Paper size is a local print/view preference and is never written into the
  Nostr event, so a flyer reprints on either size unchanged.
- Add paper_size/paper_letter/paper_a4 labels for all 14 languages.

Docs + tests:
- Spec gains a "Print paper size" section; AGENTS.md records the
  not-in-payload invariant; README adds a feature bullet; the field-limits
  cheat sheet gains Letter/A4 columns (re-measured per paper size).
- Playwright: add tests for navigator.languages inference, region-based paper
  default (A4 and Letter), and override persistence. 54 tests pass across
  Chromium, Firefox, WebKit, and Android/iOS mobile emulations.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-09 15:56:25 -04:00
thePR0M3TH3ANandClaude Opus 4.8 c5b2c20fc9 Add cross-browser e2e tests, field-limit cheat sheet, and docs
Verification:
- Add Playwright harness (e2e/) covering Chromium, Firefox, and WebKit plus
  Android/iOS mobile emulations. Smoke tests assert: default flyer renders
  with no JS/console errors, @media print hides app chrome and keeps the
  flyer, anonymous npub generation (WebCrypto), legacy /nostr/ redirect
  preserves query+hash, and event-lookup URLs show the loading state instead
  of the default flyer. All six projects pass.
- package.json/playwright.config.js wire up the projects and a managed static
  server; .gitignore excludes node_modules and Playwright output.

Docs:
- docs/flyer-field-limits.md: per-script character cheat sheet for each flyer
  field (single-line caps + multi-line guidance), measured by rendering each
  script at print size.
- README: add a Features section matching the shipping client and a
  cross-browser verification section; link the cheat sheet.
- AGENTS.md: document the e2e suite and the one-time WebKit Linux dep.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-09 15:29:49 -04:00
thePR0M3TH3ANandClaude Opus 4.8 562b7d1429 Show loading state on event-lookup URLs instead of default flyer
A viewer arriving via an event-lookup URL (naddr/nevent/note/hex in the
query or #fragment) previously saw the default placeholder flyer flash
before the real event resolved. Now it renders a localized "Loading
content..." state, and a "Could not load this flyer." state on fetch
failure. Default flyer content now appears only on the bare main URL.

- Add loading_content and load_failed labels for all 14 languages
- Add renderStatusPreview() rendering a minimal flyer sheet with a single
  centered, direction-aware message (no tear-offs, no QR)
- Render loading state synchronously before fetch in the URL-event path,
  failed state in the catch handler
- Add .flyer-status-message CSS

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-09 13:46:43 -04:00
thePR0M3TH3ANandClaude Opus 4.8 77bfef1bbe Serve client at site root; redirect legacy /nostr/
The /nostr/ path was a leftover from when the site also hosted a Tor
client. The app is Nostr-only now, so move the client to the site root
and shorten poster URLs to https://voxvera.org/#naddr1... (drops both
/nostr/ and the ?addr= query key, for a cleaner/smaller tear-off QR).

- Move site/nostr/{nostr-client.js,css,locales.js,vendor} to site/.
- site/index.html is now the client; site/nostr/index.html is a redirect
  stub that forwards /nostr/ to / while preserving the query string and
  #naddr fragment, so older poster URLs and printed QR codes still
  resolve.
- Carry the naddr in the URL fragment (#) instead of ?addr=; the viewer
  reads query, fragment, or the full URL, so old links keep working.
- Update README, AGENTS.md, and the spec to the new paths.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-09 10:49:36 -04:00
thePR0M3TH3ANandClaude Opus 4.8 828ac3a624 Tolerate whitespace in pasted event ID lookups
The Load Event field requires 64 contiguous hex chars, but the event ID
printed on a tear-off tab wraps across two lines, so copying it back in
pastes as two 32-char halves separated by a newline/space and failed to
match. Strip inner whitespace before matching the raw hex id, which also
tolerates a pasted "Event ID:" label prefix. naddr/note/nevent inputs are
unaffected.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-09 10:37:07 -04:00
thePR0M3TH3ANandClaude Opus 4.8 aedb381dfb Improve flyer tear-offs, shorten poster URL, adaptive tracking
- Tear-off tabs: single hyperlinked statement (no printed URL), with
  "Share and reprint." on its own line; localize "Click to open this
  poster." across all 14 languages.
- Show the full event ID, bottom-anchored on each tab, split into two
  32-char lines at max readable monospace size.
- Shorten the poster URL by encoding the naddr with a single relay hint
  instead of the full relay list (~233 -> ~171 chars) for an easier-to-
  scan QR; still a standard naddr, older URLs resolve unchanged.
- Add per-script letter-spacing policy (ls-full/ls-cjk/ls-none) to fit
  more text per line where safe, and turn tracking off for Arabic,
  Persian, and Hindi (fixes broken cursive joins / combining marks).
- Move the "field too long" warning inline under the offending field
  instead of a single bottom status line.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-09 10:13:55 -04:00
thePR0M3TH3AN 9baf5fb195 fixed mobile 2026-06-06 14:38:19 -04:00
thePR0M3TH3AN fd272888ec Move VoxVera credit below footer message 2026-06-06 14:31:14 -04:00
thePR0M3TH3AN d273843bb1 Improve flyer editing and add demos 2026-06-06 14:26:40 -04:00
thePR0M3TH3AN 20d5e64326 Localize Nostr editor status UI 2026-06-06 09:28:10 -04:00
thePR0M3TH3AN c08f8509db Truncate visible tear-off URLs 2026-06-06 09:19:23 -04:00
thePR0M3TH3AN c53f87dd95 Update default flyer copy for Nostr 2026-06-05 16:01:32 -04:00
thePR0M3TH3AN 83b4133780 Add Vercel static deployment config 2026-06-05 13:59:27 -04:00
thePR0M3TH3AN 1bda451604 Flag flyer language in Nostr events 2026-06-05 13:33:18 -04:00
thePR0M3TH3AN 5afda54a21 Keep content URL separate from poster URL 2026-06-05 10:18:03 -04:00
thePR0M3TH3AN 9aa90773e6 Remove legacy Tor app from main 2026-06-05 10:13:41 -04:00
thePR0M3TH3AN ee13d3fd03 Localize Nostr flyer side tabs 2026-06-05 10:01:57 -04:00
thePR0M3TH3AN 8619261c78 Localize Nostr language controls 2026-06-05 08:51:18 -04:00
thePR0M3TH3AN 3738204ede Keep viewer controls in layout 2026-06-05 08:29:40 -04:00
thePR0M3TH3AN fc50962bf4 Center Nostr viewer controls 2026-06-05 08:22:46 -04:00
thePR0M3TH3AN 0f94d640ac added language selector 2026-06-04 22:30:50 -04:00
thePR0M3TH3AN 46fa306c31 Localize VoxVera Nostr client 2026-06-04 22:06:22 -04:00
thePR0M3TH3AN d415eb26a3 Add VoxVera Nostr static client 2026-06-04 20:15:50 -04:00
thePR0M3TH3AN 285d730369 chore: release v0.3.12 v0.3.12 2026-03-19 22:52:47 -04:00
thePR0M3TH3AN f5403448a7 fix: stabilize docker runtime smoke checks 2026-03-19 22:49:05 -04:00
thePR0M3TH3AN 6822b8e496 fix: add docker runtime smoke job to ci 2026-03-19 22:36:36 -04:00
thePR0M3TH3AN 83e9e3d511 feat: harden docker runtime validation 2026-03-19 22:30:28 -04:00
thePR0M3TH3AN 004cce2a4f chore: release v0.3.11 v0.3.11 2026-03-19 22:05:16 -04:00
thePR0M3TH3AN ec0abd856c chore: release v0.3.10 v0.3.10 2026-03-19 21:45:02 -04:00
thePR0M3TH3AN 7724ac78a7 chore: release v0.3.9 v0.3.9 2026-03-19 21:36:04 -04:00
thePR0M3TH3AN c1a5e608de chore: release v0.3.8 v0.3.8 2026-03-19 16:53:04 -04:00
thePR0M3TH3AN 44cf452f67 chore: release v0.3.7 v0.3.7 2026-03-19 16:36:46 -04:00
thePR0M3TH3AN 4e49762a85 docs: add linux hosting validation runbook 2026-03-19 16:24:22 -04:00
thePR0M3TH3AN 2de7ea073c feat: add autostart lifecycle controls 2026-03-19 16:15:51 -04:00
thePR0M3TH3AN 2dd0cf4ed8 fix: install smoke test dependencies in ci 2026-03-19 16:03:55 -04:00
thePR0M3TH3AN 6c4d6d6aef feat: formalize platform runtime contracts 2026-03-19 15:57:50 -04:00
thePR0M3TH3AN e6ff21c069 chore: release v0.3.6 v0.3.6 2026-03-17 16:16:17 -04:00
thePR0M3TH3AN 1fcb86ac8b chore: release v0.3.5 2026-03-17 15:35:17 -04:00
thePR0M3TH3AN 2b29d71ac5 chore: release v0.3.4 v0.3.5 v0.3.4 2026-03-17 14:18:42 -04:00
thePR0M3TH3ANandClaude Opus 4.6 46360ca6ea fix(install): clean stale generated files from site dirs before rebuild
Install script now removes index.html, QR codes, and download bundles
from existing site directories before rebuild-all regenerates them.
Preserves config.json, .onionshare-session, server.pid, and logs.
Also auto-rebuilds on serve if index.html is missing.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
v0.3.3
2026-03-16 21:46:14 -04:00
thePR0M3TH3ANandClaude Opus 4.6 03b1c51e27 feat(rebuild): add rebuild-all command and auto-rebuild on install
Existing sites kept stale index.html from old templates after upgrades.
New `voxvera rebuild-all` regenerates HTML for all sites from the
current master template while preserving keys, config, and content.
The install script now calls rebuild-all automatically after install.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-03-16 21:39:34 -04:00
thePR0M3TH3ANandClaude Opus 4.6 fc628fb9a0 fix(templates): sync bundled index.html copies with master template
These files are not read by the build pipeline (which always generates
from index-master.html), but keeping them in sync avoids confusion.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-03-16 21:33:47 -04:00