Skip to content

unison: refpulse, a Unison service behind the site's Live button - #66

Merged
h0ffmann merged 1 commit into
mainfrom
feat/unison-refpulse
Oct 15, 2026
Merged

h0ffmann merged 1 commit into
mainfrom
feat/unison-refpulse

Conversation

@h0ffmann

Copy link
Copy Markdown
Owner

Draft: one step left before it's proven, the Unison compile (see below).

A button on the static GitHub Pages site starts a remote computation in Unison and draws the answer back on the page. The Live section's "Check the DOIs now" button calls refpulse, a Unison HTTP service (unison/refpulse/refpulse.u). refpulse asks doi.org about each of the book's DOIs and answers JSON.

browser (h0ffmann.github.io)              refpulse (Unison)                 doi.org
  click ──► GET /check?doi=…&doi=…  ──►  for each DOI: GET doi.org/<doi> ──►  302 or 404
  draw  ◄── 200 JSON, CORS: *       ◄──  {"checked","ran","results":[…]}

The result (last iteration)

The real site build, driven in headless Chrome over the DevTools protocol. The button was clicked and the answer drawn. The service URL was given with ?refpulse=, on a different origin from the page:

Live: 1 of 1 DOIs resolve, 302

When the service is down, the page says so and nothing breaks:

Live: refpulse did not answer

Is it proven?

Part Status How
Static page → cross-origin fetch → JSON → drawn on the page ✅ proven Headless Chrome on the real site/dist (port 8765) against a stand-in with refpulse's exact contract (port 8081). The stand-in asked doi.org for real: the book's DOI came back 302 and a made-up DOI 404. The page drew "1 of 1 DOIs resolve · 302". In an earlier run, with Zenodo reachable at build time, it drew all 5 version DOIs.
CORS without a preflight ✅ proven A plain GET with no custom header, answered with Access-Control-Allow-Origin: *. Chrome accepted it across origins.
Service down / not deployed ✅ proven "refpulse did not answer (Failed to fetch)…" and "refpulse is not deployed yet…"
refpulse.u compiles and serves ⏳ not yet It is written against the real names and types, read from Unison Share's API (@unison/cloud 27.3.1, routes 7.0.2, http 16.1.0, json 1.4.2, base 7.20.0). It is not compiled: lib.install failed in the agent's sandbox with IncompleteHeaders. Run just unison-install, then just unison-serve, and the same Chrome test will be repeated against the real Unison service.
Deployed on Unison Cloud ⏳ not yet It needs the owner's auth.login (see below).

Tests: 20 site tests pass, two of them new (parse_pulse, and the Live section is prerendered). just check and just md-quality pass.

Unison Cloud: how to deploy

The full text is in unison/refpulse/README.md.

  1. nix develop .#unison gives ucm, pinned by flake.lock (1.5.0).
  2. just unison-install: a UCM transcript makes the refpulse project in unison/.codebase/ (git-ignored), installs @unison/cloud and @unison/routes from Share, and adds refpulse.u.
  3. just unison-serve: runs it on this machine (Cloud.main.local.serve). It prints the URL. Then open the site with ?refpulse=<that URL>#live.
  4. Make an account at https://app.unison.cloud, then ucm -c unison/.codebase → auth.login.
  5. just unison-deploy: Cloud.main + deployHttp + ServiceName.assign. It prints a stable public URL; a new deploy moves the name to the new code.
  6. Put that URL in refpulse_url in site/src/closed_source_site/app.gleam. The button then works for every reader.

The free tier (pricing page, 15 Oct 2026) allows 5 services, which "deactivate every 3 weeks", 50 MB and 5k requests a month. So the button is built to survive a service that is down.

Can I run the Unison server on my own machine?

Yes, three ways:

  1. Unison Cloud's local runtime (just unison-serve). The same code and the same deployHttp, on your computer. Pages you open in your own browser can call it. Readers can't, because it isn't on the internet. Chrome is adding a "local network access" permission prompt for a public page that calls localhost.
  2. Your machine, for everyone: run it as in 1 and expose it with HTTPS through a tunnel (Cloudflare Tunnel, Tailscale Funnel) or your own server. HTTPS is required, because the site is HTTPS and browsers block it from calling plain HTTP on the internet. It's up only while your machine and the tunnel are up.
  3. BYOC: Unison Cloud on your own servers. Free for personal or non-commercial use, up to 10 nodes. It needs S3-compatible storage and DynamoDB (https://www.unison-lang.org/blog/cloud-byoc/).

Design

  • Public and read-only: at most 8 DOIs per request, no secret, no writes, and nothing in the page to steal.
  • Live, never a fact of the book: the page says so, and content.gleam and the book never read it.
  • Gleam ↔ Unison over HTTP/JSON: a small FFI fetch. The Lustre model has Idle | Running | Done | Failed, and the result region is aria-live.
  • Next, "the distributed way": the checks run one after another (about 0.5 s per DOI). Remote.fork/await (or parMap) can run them on many machines, if the forked task may call Http. That comes after the first compile.

Not checked

  • refpulse.u compiling (above). Expect small fixes once ucm sees it.
  • A risk on this machine: /etc/ssl/certs has a Bitdefender network monitor CA. TLS inspection like that may be what broke ucm's streaming download (IncompleteHeaders). If just unison-install fails the same way in your terminal, let api.unison-lang.org past it.
  • Chrome's local-network-access prompt, for the real GitHub Pages site calling localhost.

🤖 Generated with Claude Code

A static page on GitHub Pages calls a Unison HTTP service and draws
its answer: the Live section's button sends GET /check?doi=... to
refpulse (unison/refpulse/refpulse.u), which asks doi.org about each of
the book's DOIs and answers JSON with CORS *. The site works when it is
down. just unison-install / unison-serve / unison-deploy, with ucm from
a new .#unison dev shell. The browser half was proven in headless
Chrome against a stand-in with the same contract; refpulse.u is not
compiled yet (the libraries could not be downloaded here).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@h0ffmann
h0ffmann marked this pull request as ready for review October 15, 2026 15:35
@h0ffmann
h0ffmann merged commit d9348f7 into main Oct 15, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant