Repository navigation
Releases: ael-dev3/Allthing
Release list
Allthing 0.5.0
Added
-
A signed-in reader's front page ranks the casts of the people they follow. Hot and top on
/read/api/v1/following(the session's account's follows, from Hypersnap'sfeed/following, cached per account and never for another reader) and rank them as before, with Show less and favorites; a Following / Everyone menu beside the sort tabs switches to the trending feed and its time ranges (?feed=everyone). Where they follow few who cast lately, up to 40 trending casts follow theirs, and a note says so. Signed out, nothing changes; early paint skips a signed-in reader's front page. -
Show mini app casts: a switch in the account menu, over any feed that has mini app casts ("3 mini app casts hidden"), and on a mini app post. On, feeds, channels, profiles and threads show casts with mini apps or frames, each with a "Mini app · Open in Farcaster" link; Allthing still doesn't run them. Off is the default. The choice is kept in this browser and followed by its other tabs. The Worker now marks mini app casts instead of dropping them (one payload for everyone, no new requests), and the index keeps them; spam is left out as before, and search, reply previews and notifications still leave out both.
-
@mention suggestions in the composer, for casts, replies and quotes: typing "@name" lists matching people under the caret, those you follow first and marked "Following"; arrows move, Enter or Tab picks, Escape closes.
-
A reply's page shows the casts it answers as full cards, root first, each avatar joined to the next by a line, in place of the dashed "In reply to" snippet. A chain of more than three folds its middle behind "Show N earlier", which opens in place. The cards keep their media, counts, actions and hover cards, and hidden casts are left out as elsewhere.
-
The encryption for end-to-end encrypted messages, as pure, tested Rust (
allthing_core::e2e). After the browsers' X25519 agreement, it uses HKDF-SHA256, with both parties' identities, key versions and public keys in its info, and then XChaCha20-Poly1305, with a random 24-byte nonce, over the text padded to 32 bytes. The associated data binds the sender, the recipient and their key versions and keys. The tests pin a key and a ciphertext built on RFC 7748's keys, whichtests/e2e-vectors.test.mjsrecomputes with Node's own crypto. -
Verified Farcaster accounts on SpacetimeDB. Signed in, a browser's SpacetimeDB identity gets the Worker's word that it is your account (
POST /api/v1/auth/attest, an Ed25519 signature by a key of its own, the newATTEST_KEYsecret, valid 10 minutes), and the module's newverify_fidreducer checks it against the key's public half pinned in the module and marks the account verified. Once the key is pinned, only verified accounts can chat or send messages there.node scripts/attest-key.mjs | npx wrangler secret put ATTEST_KEYmakes the key;/api/v1/statussaysspacetime.attestand the public half (never the key). A test runs it on the real Worker. -
The SpacetimeDB module behind Messages (the one inbox and its end-to-end encrypted messages) is in the repository (
spacetime/), as published to maincloud asallthing, with its checks in CI: formatting, Clippy, unit tests of what it accepts, the wasm32 build and the license audit (sh spacetime/check.sh). -
Protocol test vectors for the messages Allthing signs (
crates/allthing-core/tests/message_vectors.rs). The encoder is checked against Snapchain's own test vectors (two casts from its engine tests, signed by its test key), the example like in Snapchain's HTTP API reference, and five messages on mainnet as Hypersnap served them: a like, a recast, an unlike, a quote and a deleted cast. Each is rebuilt from what the node says it holds and must hash to the very hash its signer signed, and each signature is verified against its signer's key (ed25519-dalek, a test-only dependency: Allthing signs with the browser's WebCrypto). -
The composer's bar is as compact as the Farcaster app's, and it posts with Cmd or Ctrl and Enter. Its tools are 32 px icons on the left (attach, emoji, sticker, GIF, the clock; a 40 px tap on a phone), and on the right a ring shows how long the text is, as in the Farcaster app: it fills toward a cast's 320 bytes, then toward a long cast's 1,024, turns amber in the last 75 bytes and gives way to the bytes left under 25, red and below zero past the limit (a screen reader hears it only as it crosses those lines). Cmd or Ctrl and Enter in the text box posts, but not in the GIF search, where Enter searches; a reply's button reads "Replying…" while it goes. Signed in, a post page fetches the composer's code and the GIF key once it is idle, so the first Reply opens with its bar, with no "Loading…"; a press on a touch screen starts the fetch too.
-
A reply shows what it answers, as the Farcaster app's composer does. Above the text box, which now says "Cast your reply": the author's picture, name, @username and how long ago, the first five lines of the cast (three on a phone) with its Pepes, its first four images and videos as squares of a fixed size (96 px, 64 on a phone; a video with its poster and a play sign), and "Replying to @username", with a line from the author's picture down to yours beside the box. It is drawn from the cast on the page, so opening it asks for nothing, the box has focus by the next frame, and nothing moves as its pictures load. Screen readers hear one line with the box: "Replying to @alice: " and the start of the cast. A scheduled reply you change shows its line as before.
-
Notifications in the app, for a signed-in reader. A bell beside your picture in the header counts what's new ("9+" past nine), from the first page of your notifications, read when the app starts, every 3 minutes while the page is in view, and when you come back to it.
/notificationslists them grouped as the Farcaster app groups them, under the tabs All, Mentions (mentions, and quotes that mention you) and Replies: each row has its kind's icon, up to five faces, what happened ("alice and 5 others liked your cast"), the cast's start with its Pepe, and when. A row opens its cast, or for follows the newest follower's profile, and a face or a name opens that person's profile card. What came since your last visit is tinted for this visit, and opening the page marks it all seen, so the bell clears, in your other tabs too. Load more goes further back. On a phone each face, name and tab takes a 44 px tap. Your browser keeps what you've seen, by account; the static copy has neither the bell nor the page. The page's code comes as the bell is pointed at or touched (24.9 KB at brotli 11), and the module grows by 5.6 KB at brotli 11 (494,256 → 499,893 bytes). -
Notifications, from the Worker:
/api/v1/notificationsgives a signed-in reader who liked, recasted, replied to, mentioned and quoted their casts, and who followed them, a page at a time, grouped as the Farcaster app groups them ("alice and 5 others liked your cast", "alice and 3 others followed you"). Hypersnap's notifications list a recast as one more like, and a mention or a quote as a reply: the hub's newest recasts of the casts liked tell recasts from likes, each cast tells replies from mentions and quotes, and the quotes of your newest casts, which Hypersnap doesn't list, are read on their own. Replies, mentions and quotes that are spam or mini apps are left out, as feeds leave them out. It answers only a session, for the session's own account, is cached at the edge for a minute per account, and no browser keeps it. What you've seen is kept by your browser, not the server. -
Images and videos in the composer. In a new cast, a reply or a quote, the attach button (on a phone, your photos), a pasted screenshot or files dropped on the composer attach PNG, JPEG, GIF and WebP images and MP4, MOV and WebM videos. Each uploads at once, with its progress and Remove, which stops it; one that doesn't go says why, with Retry. The cast embeds them in order, two embeds at most with its GIF, sticker or the cast it quotes, and the buttons say when there's no room. A photo's location, camera and other metadata are taken out in your browser before it's uploaded (JPEG, PNG and WebP; GIFs and videos go as they are). Handed to the Farcaster app, the draft carries them; scheduled, it keeps them. Where a server has no R2 bucket bound, the composer has no attach button and takes no files.
-
Every button, tab, chip and link answers a press at once, as an app's do: in the frame after a finger, the mouse or Space or Enter presses it, it shrinks a little and dims, and it eases back over 90 ms when let go. Nothing around it moves. A link in a line of text only dims, and so does everything with reduced motion. On a phone the browser showed no press at all (Chrome shows
:activeonly once a touch has lasted a moment, Safari only on pages that listen to touches), and the Cast button's and the heart's eased in, so their first frame showed nothing; the smoothness check now finds all 22 controls it presses, on the front page, in the composer, on a post and on a profile, answering in their first frame at 390 and 1280 px. A tap on a control is no longer taken for half of a double-tap zoom. -
Image and video attachments, the Worker's part.
POST /api/v1/mediatakes a signed-in reader's PNG, JPEG, GIF or WebP image (up to 10 MB) or MP4, MOV or WebM video (up to 50 MB), sent from Allthing itself, and keeps it in R2;https://allthing.social/m/<fid>/<id>.<ext>, the URL a cast embeds, serves it to anyone, in any Farcaster app: in ranges (as Safari's video player asks), with a year's caching, only ever as the type it was uploaded as, withnosniffand a sandbox policy, so a file that isn't what it says can't run as a page. The Worker hands the upload's body to R2 as it comes and never reads it, so an upload takes the same CPU time at any size: the Worker's WebAssembly memory stayed at 1.31 MiB through two 45 ...
v0.4.0
- Frame-like metadata inside fallback raw-text elements, including
noframes, no longer hides an ordinary linked post. Actual metadata following the fallback element is still detected. - A rate limit or unreadable upstream page no longer marks an indexed backfill feed as finished. The indexer keeps its cursor and retries it on a later turn; genuinely missing feeds still finish.
- Video sound controls use native focus behavior. Browser tests activate already visible controls without automatically scrolling them first, preserving the viewport while checking sound, focus and keyboard activation.
- Queued video events now respect the element's current playback state. An old pause event cannot hide sound controls after playback resumes, and an old playing event cannot show them for a paused video.
- The static copy reads linked-page metadata only up to its byte limit and cancels the remaining body. A large or stalled tail no longer delays a usable head or requires buffering the full response. CI also checks the static payload builder.
- If the app module or WebAssembly fails to load, the loading glow gives way to an accessible error message and Reload button. Reload preserves the requested page and query.
- Quoted casts have their linked pages checked for mini-app metadata, sharing the build's twelve-link quota with ordinary casts. Locally deferred checks remain eligible on the next build, and carried quote verdicts survive feed refreshes.
- The browser response cache now enforces its 200-entry capacity even when all answers are fresh. Static text uses the same bound, with expired entries removed before the oldest remaining answer.
- Wrangler's development image decoder uses Sharp 0.35.5, which includes the patched librsvg for GHSA-wq5f-xc86-pv6w. An override keeps the existing Wrangler and Miniflare versions; browser CI rejects high-severity npm advisories.
- Playing an autoplaying HLS video reuses its buffered source when it is already the best rendition the stream can supply. Low-resolution videos no longer restart the same rendition to satisfy an unavailable higher resolution; genuine upgrades still work.
- Leaving or replacing an MSE video cancels its rendition playlists and segments, pending source/buffer event waits, and idle buffering timer. Removed players no longer keep those operations alive.
- Ordinary linked pages are no longer mistaken for mini apps because a comment, script, template, or quoted attribute contains a metadata example. Actual frame and mini-app declarations in the page head still apply.
- The newest feed keeps stale-if-error recovery when both event shards are unavailable, rejects error objects as event pages, and accepts genuinely empty streams. Invalid event timestamps are skipped without overflow.
- Changing the front-page time range starts that range's pagination immediately. Requests and retry timers from the previous range cannot block it, hide its loading state, or append an obsolete page.
- Editing the header search immediately hides suggestions for the previous words, and Escape also dismisses a lookup that is still loading. Arrow keys and Enter cannot open a result from an older query.
- An API answer with valid JSON but missing or invalid payload fields is requested again on the next visit. Malformed prefetched payloads are discarded instead of keeping the page in an error state for five minutes.
Added
- Past month, Past year and All time are real, distinct ranges, ranked from Allthing's own index of notable casts in Cloudflare D1. A cron job reads one page of casts a minute: the past week's trending casts, and channels' and notable authors' casts further back in time. It keeps the casts whose points could rank, and every few minutes ranks each range's top 1,000 inside D1. The front page reads one ranking row and the page's casts, fresh, in one bulk call, and says how many casts the range was ranked from and how far back they go. Until a range can be ranked, it shows the past week's trending casts and says so. The indexer keeps to half of D1's free daily limits, and
/api/v1/statusshows its progress. The static copy has no index and leaves these ranges out. - Video sound sticks: once the reader turns a video's sound on (its badge, its controls, or a click to play it), later videos autoplay with sound until they mute one. The choice is kept in the browser's local storage. The "Sound off · tap for sound" badge on an autoplaying video is now a button that turns its sound on or off in place. Where the browser won't play sound before the reader has interacted with the page, the video plays muted and its sound comes on at their first tap or key press.
- Profile cards: hovering a profile picture or name, or resting keyboard focus on it, shows a small card about the account: its picture and names, the start of its bio, followers and following, when it joined, its Farcaster vote, the Farcaster app's label and its wallet activity by chain, with a link to the profile. They are on names in feeds, quoted casts, posts and replies, the full list of likes and recasts, people found by search and a channel's hosts. On a touch screen a tap shows the card, and its "View profile" link opens the profile; a mouse click still opens the profile directly. The bio, counts and join date come with the page's payload, from the accounts its casts arrive with, at no extra request; the vote and the wallets are read when a card opens, and a card never asks for the whole profile.
Changed
- The Farcaster vote is called the NS vote (Network School), with the NS icon beside it and a tooltip that explains it, on profiles and hover cards. The answers are unchanged: Voted yes, Voted no or Didn't vote.
- Wallet activity charts leave out chains under 1%, so each row shows a whole percent and a chain at 100% is the only row. An EVM chain that couldn't be read is named as missing instead of silently dropped from the shares.
docs/WALLETS.mdrecords the sources, the method and the explorer cross-checks. - Like and recast popovers on feed cards open with their people already listed, without "Loading…": the cards in view load them ahead, six cards per request (
/api/v1/engagement, edge-cached; one at a time, at most four per page view, and none on touch screens, with Save-Data or on 2g, or in the static copy). A replies popover reuses the post that hovering its card prefetched. Who liked or recasted a cast is now listed newest first across Hypersnap's shards, not just within the last one. - Profile cards open after 100 ms instead of 350 ms, and complete: the vote and wallet activity of the authors of feed cards in view are prefetched (two at a time, at most 12 accounts per 5 minutes, only where a pointer hovers), so an opened card has no loading state.
- Profiles' wallet activity includes Solana: the transactions involving the account's verified Solana addresses (up to 1,000 each, from Solana's public RPC) count alongside EVM chains, so the chart reads like "Base 44% · Solana 55% · other". The static copy still can't read Solana, whose RPC refuses browsers.
- Post pages drop the "Posted via" box. The posting app is a quiet line in the post's meta ("via Farcaster"), and its signing key and app fid show on hover.
- Profiles say when the account joined. Accounts registered before the ID registry moved to OP Mainnet in November 2023 showed "before Nov 2023", the earliest date onchain data has for them; they now show when they got their first username (dwr: 17 Jun 2021), read from the fname server's public transfer log, which the old sign-up registered along with the account. If that read fails, the profile keeps the date its previous copy had and is rebuilt within a minute. Newer accounts show their onchain registration, as before.
- The front page has a Reddit-style time filter next to hot and top: All time, Past year, Past month, Past week, Today (the default) and Past hour, kept in the URL as
t. Up to a week, it picks the window Hypersnap ranks trending casts over: 1 hour, 24 hours or 7 days. Past month, Past year and All time are ranked from Allthing's own index (see Added). A link with a time range still costs one API request, started by the inline script. - Pull requests run deterministic browser regressions against the local build, alongside the Rust checks. Browser failure reports are retained for 7 days; live production performance checks keep their separate job.
- Feeds show each cast's whole text, with its paragraphs, line breaks, links and mentions, instead of its first 120 characters as a one-line title. A cast with only media shows just its media, without an "[image]" or "[video]" placeholder.
- The front page's data request starts from a small inline script in
index.htmlas soon as the page arrives, alongside the WebAssembly download, instead of after the app starts. It is still one API call. - Media is bigger. A single image or video takes its own shape, as wide as the card and at most 70% of the window tall (80% when portrait), instead of at most 580 px wide and 460 px tall. Its frame fits it, so a narrow image has no bars beside it, and a portrait video is no longer letterboxed in a 16:10 box. Back to a feed still lays images out at their size at once, and now videos too.
- Pages no longer say "Updated N min ago". The notice that a cached copy is shown because the data source isn't answering stays.
- Feeds show a quoted cast inline, as a small card with its author, text and media, instead of a "↳ quotes a cast" chip. The quoted casts come in the same payload as the feed (one bulk read per page build), so the card is there at once. A click on it opens the quoted cast. A quoted cast that is spam or a mini app says so instead of showing its content, there and on post pages.
- Search results stay compact: the first 6 people and 8 channels, laid out in columns with a count on each group, and "Show all" to see the rest. Casts start above the fold, and their text no longer takes the group h...
v0.3.0
Allthing 0.3.0: unified search, who-liked/recasted/replied popovers, wallets popover, Farcaster vote on profiles, instant navigation (feed-data reuse, prefetch, view transitions, scroll restore), muted autoplay video in view, server-side mini app / frame filtering, and a much more minimal UI. Full notes: https://github.com/ael-dev3/Allthing/blob/main/CHANGELOG.md
Allthing 0.2.0
0.2.0 line: Rust on Cloudflare Workers rewrite.
Allthing 0.1.0
0.1.0 line: original Allthing prototype (GitHub Pages).