Tags

Tags give the ability to mark specific points in history as being important
  • v2.6.2

    protected
    v2.6.2
    
    The confirmation dialog says what it is about to do, and condenser's own
    bookkeeping stops travelling to the signer (MRs !204, !205, !206).
    
    - The dialog named the chain's operation rather than the action the reader had
      asked for. Muting a post, flagging it, pinning it, following an account,
      subscribing to a community, changing a role, setting a user title and editing
      community settings are all `custom_json`, so every one of them asked for
      confirmation of "Custom Json"; claiming rewards and deleting a post named
      their raw operation too, and a post published from the assistant was called a
      comment. The action is carried inside the operation's payload, so the dialog
      reads it from there and names it. An explicit title still takes precedence, so
      the ones already set by the editor, the voting control and the reblog control
      are unchanged, and an operation with nothing mapped still shows its raw type
      rather than being given an invented label. Eighteen titles across the nine
      locales.
    
    - Undoing a follow and undoing a mute are the same operation on the chain: a
      `follow` whose `what` is empty. Nothing in the payload tells them apart, so
      unmuting an account asked the reader to confirm unfollowing. The two callers
      now name the action themselves, since they are the only place that still knows
      which one was clicked.
    
    - `__config` carries condenser's own bookkeeping -- the title the dialog shows,
      the body an edit patches against, the message to raise on success -- and it was
      reaching the signer on every operation except a comment. A signing extension
      serializes the fields it is handed while the node re-serializes only the ones
      it knows, so the two disagree and the signature fails to validate; the comment
      path already rebuilt its operation without it for exactly that reason, and a
      reblog has carried one on a `custom_json` for some time. The broadcast now gets
      a copy without the field, and the originals are left alone for the hooks that
      read it afterwards. The plain-key path was never affected, which is why it went
      unnoticed.
    
    Note that v2.6.1 was tagged but never deployed, so a release from here carries
    the reply invitation of that tag as well.
  • v2.6.1

    protected
    v2.6.1
    
    One invitation to reply, and the same one wherever a reply can be written
    (MR !203).
    
    - A post offered two ways into the same editor: the footer's reply arrow and
      the box that says "post your reply", a few pixels apart. The box wins while
      it is on offer, because it is the one that says what it does, and the arrow
      comes back once the editor is open, where it is no longer a duplicate but the
      way to close it again. Both controls now read one exported, pure rule instead
      of each testing its own conditions, and a sweep of every combination asserts
      they are never on screen together -- testing each condition separately is
      exactly how they drifted into overlapping.
    
    - With the arrow hidden, a post whose reader can do nothing else to it, neither
      the author nor a moderator, still got the footer's action tab drawn around
      nothing. The footer decides by whether it was handed actions, and a JSX
      element is truthy however little it renders, so it cannot work this out for
      itself. It is told instead.
    
    - A reply card offered an arrow where the post above it offers a box: the same
      action presented as two different things to learn. The invitation now lives in
      a component of its own, with its own stylesheet, so the post and the card
      cannot drift apart, and the rule that keeps the arrow and the box from both
      appearing travels beside it. Only where an inline reply is actually on offer,
      which is the replies tab and nowhere else; a feed card is untouched.
    
    - The block holding the invitation carried no `order` inside `.articles__content`,
      a flex row whose children are ordered explicitly on a scale that differs by
      breakpoint. Taking 0, it sorted ahead of the footer and shrank into a flex item
      beside the body, so the box floated mid-card with empty space around it. Past
      the largest footer order at full basis, it lands under the footer at every
      width.
    
    - On a card the invitation takes the step and the dotted rule the threaded view
      already uses for a nested reply: 62px in, collapsing to 10px on a phone the
      way comments do, and stopping at 34rem so a wide screen gives it air rather
      than more box.
  • v2.6.0

    protected
    v2.6.0
    
    Feeds that no longer take a single node's word for what exists, an editor that
    tells the author what will happen before it happens, and the refreshed Terms of
    Service and Privacy Policy (MRs !182 to !202).
    
    - A post now has to be corroborated by two verified nodes before it appears in a
      feed. The chain leaves no tombstone for a deleted post, so a feed built from
      whatever one node returns keeps showing posts that no longer exist;
      corroboration is what stands in for the tombstone. The author's own post is
      the exception and appears as soon as one node has it, because the author
      already knows it exists. A merged feed is ordered from a single node rather
      than by rank across nodes, which is what keeps the order stable.
    
    - A node that is down no longer holds up the reader's first feed. The pool waits
      for a quorum, not for everyone, so one unreachable node costs latency and not
      a blank page.
    
    - A deleted post stays deleted whatever a stale node serves, and deleting one
      now reports progress and confirms, instead of appearing to do nothing while
      the operation is in flight.
    
    - The editor rejects a beneficiary that is not an account. A name that is not an
      account makes the chain reject the whole transaction, so the post never
      published and the author was told nothing: the editor reported success and the
      post was gone. Names are now checked before the transaction is built, in the
      field where the mistake was made, and a lookup that fails leaves the name
      undecided so a node having a bad minute can never block a publish.
    
    - The confirmation dialog names the operation being confirmed instead of the
      chain operation behind it.
    
    - Voting says more and guesses less: a closed payout window blocks the vote and
      explains why rather than failing silently, and a vote cast after the window is
      presented as a signal rather than as a mistake, naming the account and what is
      missing.
    
    - New in the reading and writing surface: an open reply box under every post, a
      lightbox for the images in a body, a back-to-top button on long views placed
      clear of the assistant's corner, and the author landing on their posts tab
      after publishing instead of somewhere they have to navigate back from.
    
    - Leaving the editor no longer depends on the browser's history behaving, which
      is what trapped authors on iOS, and a collection stored as null is no longer
      read as an absent collection, which is what made parts of the interface render
      empty rather than render nothing.
    
    - A submit that throws can no longer leave the form stuck behind its spinner,
      and a failover is verified before anyone is warned about it, with the client
      failing back once the node answers again.
    
    - The Terms of Service and the Privacy Policy are rewritten: plainer language,
      neutral governing law, the scope extended to the wallet, Blurt Dollars and an
      inherited DMCA address removed, and a Cookie Disclosure stating that only
      session cookies are used, with no third-party or tracking cookies. Contact
      addresses across the application now use the official @blurt.blog mailboxes.
    
    - The fake Blurt airdrop links are blocked in the phishing list. Both forms of
      the link were added to the three copies of that list, which is what stops the
      copies from drifting apart again.
    
    - Interface work across the application, most of it on phones: the posts header
      keeps its controls on screen, the stacked mobile header row gets its spacing
      and its side, the view and reblog glyphs preview what a click will bring
      rather than what is already the case, comment and reply footers match the post
      editor's layout, the back button closes a modal instead of navigating away
      with one still open, and the follow pills on a cover keep readable text in
      dark mode. The translations are finished for every string the application
      actually uses.
    
    - A scheduled pipeline now runs only the environments watchdog, which compares
      what each environment serves against the history of the default branch and
      fails when an environment serves something that branch does not know.
  • v2.5.0

    protected
    v2.5.0
    
    Post templates move from the browser to the account, and the dialog they are
    edited from is usable on a phone (MR !181). Requires nexus-go v2.2.0 or later,
    which is deployed on both nodes.
    
    - Templates lived in localStorage under blurtPostTemplates-<user>, so they were
      per-browser and per-device, lost with site data, and absent from a private
      window. They are now rows in nexus-go, reached through five signed RPCs.
      Every operation is signed on its own and deliberately does not use the draft
      session grant: that grant is a bearer credential meant to spare a signature
      on each draft autosave, and template operations are rare enough to pay for
      themselves.
    
    - Writes fan out to every node and reads merge what every node returns, with
      last-write-wins and tombstones for deletes, exactly as drafts do. A list from
      zero reachable nodes is UNKNOWN rather than "you have none", so an outage
      never looks like data loss.
    
    - A modal asks once, before the editor stops reading the browser's copy, and
      clears that copy only after every template is confirmed stored. It was the
      only copy.
    
    - A /templates page, sibling of /drafts and beside it in the account menu,
      lists what the account holds, opens one into the editor, and deletes with a
      confirmation that says the delete cannot be undone -- because it cannot.
      Deleting used to sit in Advanced Settings as a second button identical to the
      one next to it and one click from Load.
    
    - The beneficiary row is no longer a twelve-column grid on a phone. Splitting
      those columns between a three-character percentage and an account name never
      worked at any share: the percentage clipped or the name was cramped. The
      percentage now takes exactly the width three digits need and the name takes
      the rest. Everything in the row centres against it, the @ box is transparent
      rather than Foundation's near-white, and Advanced Settings has a Cancel
      button at all -- its only way out was the corner X.
    
    - Adding a beneficiary over an unfinished row used to produce a second blank
      row and a duplicate-beneficiary error, because two blank rows cannot be told
      apart. Add is hidden while any row lacks a name or a percentage.
    
    - The template picker and the community role picker no longer hand the choice
      to the operating system's own picker, which cannot be styled and covers a
      dialog with a sheet that looks nothing like the site.
    
    - Below 640px a site-wide rule gives every table display:block so it can
      scroll, and a block box shrinks to its content -- so a narrow table stopped
      filling its card and read as one that had failed to load. The templates list
      and the community roles table use a wrapper that keeps the table a table.
    
    - Two i18n sections, post_template_selector_jsx and beneficiary_selector_jsx,
      existed only in English and rendered that way whatever the reader's language.
      Both are translated into the other eight locales. Add account and Remove now
      say Add beneficiary and Remove beneficiary; Sign in and Sign up, a letter
      apart and neither saying what it does, say Login and Register.
    
    Also in this release, with no effect on what users see: the repository gained a
    jest configuration and a green suite where twelve of its suites had been
    failing, a CI job that runs it on every merge request with the results and
    coverage shown there, and an npm install that survives a socket timeout instead
    of failing the pipeline before anything is tested (MRs !177, !178, !179, !180).
  • v2.4.1

    protected
    v2.4.1
    
    The editor's beneficiary shares, built and announced from one place (MR !176).
    
    - The editor announced beneficiary shares with arithmetic that disagreed with
      what it went on to broadcast: the dialog counted the form rows while the
      operation was assembled separately, so the two could drift. Across the
      configurations the validator accepts, the announced total was wrong in about
      one in five, and some announcements added up to more than 100%, which the
      chain cannot represent. The summary and the operation now come from a single
      buildBeneficiaries(), so what the author is shown is the list that is sent.
    
    - Automatic shares stay fixed. The community's and the referrer's 3% are never
      reduced, trimmed, or edited away. What the author adds is added on top, and
      when the total will not fit it is the author's own shares that yield,
      largest first. An account earning several shares becomes one entry with
      their sum, which is what the chain requires: it rejects a beneficiary listed
      twice. The convention is client-side and so unenforceable in the browser;
      the editor at least no longer offers the reduction itself.
    
    - The summary names each share's origin -- community, referrer, or set by the
      author -- on its own line, in place of the aggregate counter. The count
      corrected by !169 can no longer go wrong because nothing is counted any
      more: every share is listed with the amount it actually contributes.
    
    - validateBeneficiaries() now measures the author's shares against what is
      left, taking both the automatic percentages and the slots they occupy out of
      the budget, so validation and construction agree rather than each guessing.
      It also tolerates lists that reached it without passing through the form,
      which a hand-edited page can produce.
    
    - Applying a loaded template writes to the editor's store, not only to the
      dialog, so a template's beneficiaries survive being loaded.
  • v2.4.0

    protected
    v2.4.0
    
    Security and availability fixes for the AI proxy and server-side rendering,
    plus the beneficiary lines in the editor (MRs !168, !170, !171, !172, !173,
    !174, !175).
    
    - Security: the AI proxy took the upstream endpoint from the request body and
      passed it to fetch unchecked, so anything the server could reach a caller
      could reach through it, with the response returned verbatim. Endpoints now go
      through a guard that allows https only, refuses addresses that are not
      publicly routable, and rejects redirects, checking the address inside the
      connection's DNS lookup so it covers the one actually connected to. The
      routes also require the same CSRF token the rest of the API does, which stops
      the proxy being usable as an open relay towards any public host. node-fetch
      becomes a direct dependency at 2.7.0: it was present only transitively at
      1.7.3, which leaks the Authorization header on a cross-host redirect.
    - The AI proxy had never worked in a container. fetch was used but never
      imported, and the image runs Node 16, which has no global fetch, so every
      call answered 500. Fixing that alone would have armed the hole above, so both
      land together.
    - Rate limiting: rateLimitReq ignored its own limit argument, keyed on a header
      the client writes, and never expired its entries. It now takes a per-minute
      budget counted per route over a sliding window, identifies the visitor by
      CF-Connecting-IP where the deployment declares it is behind Cloudflare, and
      evicts the oldest entries at its ceiling rather than turning new callers away.
    - Availability: a failing remote document no longer kills a worker. The pinned
      posts, dapps and nsfw lists are fetched from raw URLs, and a redirect, an
      HTML error page or a rate-limited RPC each produced an uncaught exception or
      a promise that never settled. Fetching is now one helper that always settles,
      entries that cannot be read are skipped rather than failing the batch, each
      loader keeps its last good value, and an unhandled rejection is logged
      instead of ending the worker.
    - The AI model picker asks the provider which models the key may use instead of
      offering a list compiled into the client, which had gone stale to the point
      where every option failed. The model can always be typed. Upstream failures
      answer 400 with the provider's message rather than a status a CDN replaces
      with its own error page.
    - The AI chat now streams: the event stream was being gzipped, which held the
      answer back until it was complete. Reasoning models no longer print their
      chain of thought as the answer, the settings screen and the chat share one
      configuration store, and code spans are legible in the dark theme.
    - Logging out clears the assistant. Its conversation and the provider API key
      live in the browser, so both survived a logout and the next person at that
      machine could read the chat and spend the key. Expanding the chat window no
      longer hides its own toolbar behind the site header.
    - Editor: the beneficiary lines print a total when more than one automatic
      share applies and name the origin of each, instead of showing two identical
      lines that contradicted the counter. Translated into the remaining locales.
  • v2.3.4

    protected
    v2.3.4
    
    Two vote-reliability fixes (MRs !165, !166).
    
    - Vote: a broadcast error no longer erases the user's own vote unless the node
      contradicts it. blurtjs retries the same signed transaction, so a vote that
      was already applied comes back as "identical to this vote", and a timeout or
      a 5xx reply gives no verdict at all; both used to clear the optimistic vote,
      emptying the heart for a vote that was on chain and carrying that stale copy
      between views. The rollback now asks the node who voted before reverting, and
      never reverts when the node cannot answer.
    - RPC: failover now fires when a node is unreachable. transport.send() reported
      its failures only through its own callback, so error_count never reached
      failover_threshold and the client stayed pinned to a dead node. The retry
      budget is bounded too (it was the library default of 10, backing off to about
      17 minutes), so the threshold is reached in seconds.
  • v2.3.3

    protected
    v2.3.3
    
    Two fixes to the reply flow.
    
    - A reply posted from a list card appeared above the post it answered. The list
      layout orders the card's children explicitly and the panel had no order of
      its own, so it sorted ahead of everything; it now sits past the footer at
      every breakpoint.
    - Opening a post that was not in the store yet showed the not-found page until
      the data arrived, most visibly through "view the full context", where the
      parent is never in the store. The page waits while the request is in flight
      and reports the post missing only once it has finished without producing it.
  • v2.3.2

    protected
    v2.3.2
    
    ESLint cleanup and the genuine errors it surfaced (MR !161).
    
    - Config: demote airbnb style rules to warnings (14,895 -> 0 errors); ignore
      vendored assets; declare jest/browser globals. CI-only, no runtime effect.
    - Fixes: latent bugs the linter had buried (undefined refs in FetchDataSaga,
      ChangePassword, Header, tape, SteemMarket, App, ...), all behavior-preserving.
  • v2.3.1

    protected
    v2.3.1
    
    Show the post-footer voters arrow whenever a post has votes.
    
    - The vote-count + voters arrow was gated on active_votes being pre-loaded in
      that copy of the post, so it appeared for some accounts/posts and not others.
      Gate it on total_votes instead; the modal already fetches the real voters on
      demand when opened (with reference-node fallbacks).
  • v2.3.0

    protected
    v2.3.0
    
    Friendly, localized error handling; mobile and deploy improvements.
    
    - In-app error toasts now show calm, human, localized messages instead of raw
      chain assertions, mapping the common Blurt transaction errors (with a generic
      fallback) across all nine locales.
    - Fix long error messages overflowing the viewport on mobile.
    - Remove the obsolete out-of-bandwidth / Resource Credits modal, which does not
      apply to Blurt (transactions are gated by fees, not a resource-credit allowance).
    - Version the per-environment deploy config in the repo so the deploy jobs ship it.
    
    Classification: MINOR — user-facing features and fixes, no breaking changes.
  • v2.1.0

    protected
    v2.1.0
    
    - Server-side post drafts
    - Accept .mov / HEVC video uploads (by extension; no client-side rejection)
    - Drop the obsolete HEVC upload guidance
    - Logged-out navigation: keep Sign in / Sign up in the side menu, open login as an in-app modal, retire /login.html
    - Show the header Sign in / Sign up CTA only from 1200px (fix the iPad-landscape overlap with the primary nav)
    - Hide the Follow button for logged-out visitors
    - Correct the sign_in mistranslation (es, fr)
    - Centre the video play badge on iOS in the expanded list view
  • v2.0.0

    protected
    v2.0.0
    
    First tagged release. Introduces tag-driven releases: production is deployed from version tags, and /__version.json reports the tag.