Skip to content
LogoLogo

Changelog

Methodology

0.17.0

BREAKING CHANGE: Community membership (protocol 14.0.0)

Joining and leaving a community are signed messages now, so every node knows who is a member: communities you joined follow you to every device, a community's owner learns who joined, and member counts are real. See "Membership" in the protocol docs.

Breaking Changes

  • Protocol versions bumped:
    • Pubsub topics (roots/messages): 13.0.0 → 14.0.0
    • Protocols (leaves/levels): 16.0.0 → 17.0.0
  • New message types join and leave, signed as Membership(string type,string community,uint256 timestamp), with community a community's ID. Nodes accept one only if the community exists in a version older than the message, like a post to it, whatever the signer's earlier joins and leaves.
  • A person's newest join or leave per community decides (of equal timestamps, the greater message ID). Joins signed with a delegated key count for the account. A community's owner is always a member.

Cache

  • New memberships table: per signing key and community, the newest join or leave. Members are resolved through the delegations when they're read. A join or leave goes into it before the trie and is taken out again if the trie write fails; at startup the primary puts every join and leave of the trie into it again (one walk of the trie for communities, joins and leaves).
  • store.leaves and store.posts take a list of types.

API

  • POST /api/v1/list takes type: "join" and type: "leave".
  • Community objects have members (the owner included). GET /api/v1/communities?member=<address> lists the communities someone joined or owns; it isn't cached.
  • When the node's API takes a join, the community's owner gets a push notification ("New member", <name> joined <community>) and a Farcaster mini app notification, with the rules of upvote notifications. Leaves, the owner's own joins and joining again within a day don't notify.
  • GET /api/v1/activity has joins: who joined your communities. The notifications page shows them grouped per community.

Migration

All nodes must upgrade together. A 14.0.0 node will not sync with a 13.0.0 node. The website and the apps still keep joined communities locally until they sign joins and leaves.

Communities: read API and feeds (no protocol change)

  • GET /api/v1/communities (q, owner, limit) lists communities, most active first, and GET /api/v1/communities/:owner/:key returns one, or 404.
  • A community's feeds are namespaced by it: /api/v1/communities/:owner/:key/feeds/hot|new|best and the rows endpoints under the same path. They're Kiwi News' Hot, New and Best with only the community's stories, ranked with the same rules. An invalid ID is a 400.
  • Kiwi News' feeds (also /, /new and /best on the website) are one feed: they have every story, a community's ones included, ranked as always.
  • Stories in the feeds and from GET /api/v1/stories have community: null or {id, name, icon, color}. The cache hands the column on as communityId, so the bare ID no longer goes out as community anywhere.
  • /c/* in the universal links.

Communities: web UI (no protocol change)

  • A community's pages: /c/<owner>/<key> (Hot), /c/<owner>/<key>/new and /c/<owner>/<key>/best, with its header card and house rules; with ?via=<address>, the page an invite link opens.
  • /communities lists them (Discover, most active first, with search, and Joined) and /communities/new starts one, or edits yours, with a live preview, icon suggestions from its name and a picker of Phosphor's icons.
  • Joining keeps a community in the browser (it isn't on the protocol): the feeds show the communities you joined as tabs and in the side box.
  • Stories show their community's tag before the domain.
  • Submit: "Post to" picks Kiwi News or one of your communities; a post to a community is signed as the 5-field Message.
  • "Communities" in the navigation.

Fixes (communities, no protocol change)

  • A community's owner is resolved through the delegations when a post is judged or the community is read, not when the node stored it. Before, a node that stored a community before it knew the signer's delegation kept it under the delegated key, and nodes could judge posts to it differently forever. The communities table is now keyed by signer and name; the old table is dropped on start and refilled from the trie.
  • A node loads its delegations before it takes messages over p2p or the API.
  • A community goes into the table before the trie, and is taken out again if the trie write fails, so a failed cache write can't leave a node rejecting its posts.
  • Refilling the communities table at startup runs in batches and yields, and skips broken entries.

0.16.0

BREAKING CHANGE: User communities

Anyone can now run a community and post to it. A community belongs to the identity that signs it and lives in its namespace (<owner address>/<name key>), so creating one needs no consensus. See "Communities" in the protocol docs.

Breaking Changes

  • Protocol versions bumped:
    • Pubsub topics (roots/messages): 12.0.0 → 13.0.0
    • Protocols (leaves/levels): 15.0.0 → 16.0.0
  • New message type community, signed as Community(string type,string name,string about,string[] rules,string icon,string color,uint256 timestamp): a name (24 characters), a one-line description (100), up to 5 house rules (140 each), a Phosphor Icons 2.1.1 icon and a hex colour (#RRGGBB). Signing one again under the same name updates it.
  • Amplify gets an optional community (<owner address>/<name key>). With it, the message is signed as the 5-field Message(string title,string href,string type,uint256 timestamp,string community). Nodes accept it only if the community exists in an older version. Upvotes and comments are unchanged.

Cache

  • New communities table (newest version of each community, and when it was first created) and a community column on submissions, added to existing caches on start.
  • POST /api/v1/list takes type: "community".

Migration

All nodes must upgrade together. A 13.0.0 node will not sync with a 12.0.0 node. Feeds don't treat community posts differently yet; that comes with the clients.

0.15.0

BREAKING CHANGE: Chunk trie sync so one frame no longer grows with the tree

Sync used to put every node of a level, and then every missing leaf, into a single length-prefixed message. That payload grew with the trie, which is why maxDataLength went 4MB → 8MB → 16MB → 32MB.

A level and its missing leaves are now sent as several frames of at most 1 MiB. Each frame is the same CBOR array as before, so the receiver stores it the same way. maxDataLength is 2 MiB: a ceiling for one frame, not for the trie. It does not need to move again as the tree grows.

Breaking Changes

  • Protocol versions bumped:
    • Pubsub topics (roots/messages): 11.0.0 → 12.0.0
    • Protocols (leaves/levels): 14.0.0 → 15.0.0

Migration

All nodes must upgrade together. A 15.0.0 node will not sync with a 14.0.0 node.

0.14.0

BREAKING CHANGE: Add text post support with inline content

This release adds support for submitting text-only posts using data:text/plain, URLs, allowing users to share thoughts and discussions directly without requiring external links.

Breaking Changes

  • Protocol versions bumped:
    • Pubsub topics (roots/messages): 10.0.0 → 11.0.0
    • Protocols (leaves/levels): 13.0.0 → 14.0.0
  • Validation schema updated:
    • href field now accepts data:text/plain, URLs (up to 2048 chars)
    • Pattern: ^(https?://|data:text/plain,|kiwi:0x)

New Features

  • Text Post Submission: New textarea in submit form with 2048 character limit
  • Inline Storage: Text content stored as data:text/plain, URLs with encodeURIComponent encoding
  • Story Display: Text content renders on story pages between subtitle and action buttons
  • Feed Behavior: Text posts appear in feeds without preview (consistent with comment links)
  • No Domain Badge: Text posts don't show domain badges (data: and kiwi: URLs)
  • Preview Generation: OG images generated with empty domain for social sharing

Security

  • DOMPurify sanitization at all rendering points
  • Frontend validation: maxLength constraint (2048 characters)
  • Safe encoding: encodeURIComponent for storage
  • Title sanitization: getSlug() uses DOMPurify

Bug Fixes

  • Fixed frame image path generation (was incorrectly stripping extra characters from index)

Migration

All nodes must upgrade to maintain P2P connectivity. Nodes on different protocol versions will not sync with each other. Text posts will only be recognized by nodes running 0.14.0+.

0.13.0

BREAKING CHANGE: Increase P2P sync message size limit to 32MB

  • Increased maxDataLength from 16MB to 32MB in sync protocol (src/sync.mjs)
  • Fixes "message length too long" errors during trie reconciliation
  • Required as trie grows larger and deeper levels contain more nodes to sync
  • Protocol versions bumped:
    • Pubsub topics (roots/messages): 9.0.0 → 10.0.0
    • Protocols (leaves/levels): 12.0.0 → 13.0.0

Migration: All nodes must upgrade to maintain P2P connectivity. Nodes on different protocol versions will not sync with each other.

0.12.0

BREAKING CHANGE: Complete removal of Kiwi Pass NFT-based access control system

This release removes the entire NFT/allowlist concept from Kiwistand and simplifies to delegation-only access control.

Breaking Changes

  • Protocol versions bumped:
    • Pubsub topics: 8.0.0 → 9.0.0
    • Protocols (leaves/levels): 11.0.0 → 12.0.0
  • API Changes:
    • Removed /allowlist endpoint
    • Removed fetchAllowList() from frontend API
  • Delegator2 SDK (@attestate/delegator2):
    • eligible(allowlist, delegations, address) → resolveIdentity(delegations, address)
    • Removed all NFT-related functions: eligibleAt(), extractLegacyObject(), legacyEligibleAt(), _eligibleAt()

Removed Components

Backend:

  • src/chainstate/mainnet-mints.mjs - Mainnet NFT tracking
  • src/chainstate/mint.config.crawler.mjs - NFT mint crawler
  • src/chainstate/transfer-loader.mjs - NFT transfer loader
  • Registry functions: allowlist(), refreshAccounts(), augmentWithMainnet()

Frontend:

  • src/web/src/BuyButton.jsx - NFT purchase component
  • src/web/src/TelegramLink.jsx - Telegram integration
  • src/web/src/LeaderboardStats.jsx - Community stats
  • src/views/kiwipass-mint.mjs - NFT minting page
  • src/views/indexing.mjs - NFT indexing wait page
  • src/views/invite.mjs - Invite page
  • src/views/leaderboard.mjs - Community leaderboard
  • Routes: /community, /invite, /indexing, /kiwipass-mint

How It Works Now

  • Any address can act on its own behalf
  • If an address has delegated to another address, resolveIdentity() resolves it to the delegator
  • No more NFT/allowlist checks anywhere in the codebase
  • All identity resolution is done purely through the delegation system

Migration Guide

If you're running a node:

  1. Update to the latest version
  2. The system will automatically work with the new delegation-only model
  3. No manual migration needed - existing delegations continue to work

If you're using the API:

  • Replace calls to /api/v1/allowlist with /api/v1/delegations
  • Update any code using eligible() to use resolveIdentity()

0.11.0

(breaking) Fixed critical bug in allowlist crawler that halted allowlist progression since the Delegator3/smart account switch in August 2025.

Background: When commit 14baab66 updated the system to use Delegator3 contract (0x418910fef46896eb0bfe38f656e2f7df3eca7198), the ABI for the setup function was correctly updated to match Delegator3's on-chain interface, which only accepts bytes32[3] data as a parameter (removing the beneficiaries and amounts arrays that existed in Delegator2).

However, the transfer-loader.mjs code was not updated accordingly - it still attempted to extract and use input.beneficiaries and input.amounts after decoding. Since these fields no longer exist in Delegator3's return value, this caused the decoder to throw errors on every Delegator3 transaction, completely halting the allowlist crawler.

Impact: The allowlist stopped updating after the Delegator3 switch. Users who minted Kiwi Passes during this period (several weeks) were not added to the allowlist and could not interact with the site. All production nodes were affected.

Fix (commit c8a92a7c):

  • Removed the code that tried to access non-existent beneficiaries and amounts fields (src/chainstate/transfer-loader.mjs:67-76, 113-122)
  • Added try-catch blocks around decodeFunctionData to handle any decoding errors gracefully
  • Now correctly processes Delegator3 transactions with the updated ABI

How to upgrade your existing node?

CRITICAL: All nodes running the broken version have corrupted/incomplete op-call-block-logs-load data and MUST re-sync:

  1. Navigate to your DATA_DIR
  2. Delete the op-call-block-logs-load directory
  3. Run: DATA_DIR=/path/to/your/data npm run sync
  4. Wait until the logs show: "op-call-block-logs loader strategy has finished"
  5. Your node is now fully synchronized with the correct allowlist

0.10.1

  • Fix npm run reconcile

0.10.0

(breaking) To make Kiwi News compatible with smart wallets and EIP-4337, we've made some breaking changes to the delegator2 contract.

Instead of relying on transaction.from, it now emits msg.sender from within the contract, hence allowing paymasters etc. to call etch in the name of the Kiwi Pass controlling address.

This however means that we have, for the time being, added all other signers as a plain text file to the code base, meaning that the old delegator2 contract at 0x08b7ECFac2c5754ABafb789c84F8fa37c9f088B0 will not count anymore when delegating upvoting/commenting power to a new address. Instead all apps will have to start using the new contract: 0x418910fef46896eb0bfe38f656e2f7df3eca7198. For a proper upgrade path where signers can both be revoked and used from both delegator2 contracts we'll have to combine their logs. It's not clear to me whether all of this work would be worth it or whether it would be simpler to just call the delegator2@0.5.1 period its own epoch, add all its messages to a merkle tree and then just consider them valid if they appear as a member. For this release, I did the simplest possible thing because I felt as if this work wouldn't pay off to do properly as we're still not really making money with Kiwi.

Anyways, as the above change will interact with what messages nodes are considering valid, we've bumped the version to 0.10.0 and we've also upgraded our version identifiers in libp2p. We highly recommend all node operators to upgrade.

How to upgrade your existing node?

  1. Run npm i then run cd src/web && npm i -f
  2. In your DATA_DIR, rename list-delegations-load-2 (to back it up)
  3. Run npm run sync. It will run very briefly (this is fine)
  4. That's it, you should be good to go

0.9.0

  1. (breaking) Add token tracking to allow to precisely determine during reconciliation if someone was eligible to post during a period. For more details of what this feature consists of, read the changelogs of the dependencies:

Essentially with this release we're laying the groundwork for our later 1 pass = 1 upvote change. For more details, see this document.

If you're running a node in version 0.9.0 it is required to re-sync the node using npm run sync. For this, in your DATA_DIR, delete the op-call-block-logs folder, then run npm run sync until you catch up again with the OP mainnet chain tip. This is necessary as we have to load each chain interactions respective tokenId into the mints database.

  1. (breaking) For those who are running a kiwistand node in npm run reconcile mode, we've fixed a bug that could have allowed an upvote to be stored twice. If you've done this, please delete the data.mdb and lock.mdb file from your DATA_DIR and re-reconcile with the network from scratch.

  2. We're including many other changes in this release, none of which are supposed to be breaking. Most changes are related to the product and the front end.

This release doesn't include any breaking changes to the protocol itself, which is why we've also not bumped the protocol version identifiers. It, however, contains the above breaking changes for anyone currently running a node, which is why this release is a minor patch version increase.

0.8.0

  • (breaking) Double it-length-prefixed length
  • Fixes in reconciliation algorithm

Caution: If you're running a node and you must preserve its reconciliation data, it is best get in touch with us directly via Telegram. If you can afford to just delete the reconciliation data and start over, we suggest you do this.

0.7.0

0.6.0

  • Upgrade all libp2p version identifiers
  • Move all eligible-defining NFTs to oeth:0x66747bdc903d17c586fa09ee5d6b54cc85bbea45
  • Deprecate RPC_HTTP_HOST
  • Fix bugs with indexing logs. attestate/crawler order function was ordering logs based on blockIndex and transactionIndex, but logIndex was necessary too.
    • We've essentially created two new indexing strategies ("list-delegations-2", and "op-call-block-logs").
  • store.posts now tolerates finding "in-eligible" messages
  • Added docs that explain how Kiwi News Protocol's set reconciliation works
  • Use a DFS to traverse the tree for posts (thank you to @freeatnet!)

0.5.0

  • Deactivate revocations temporarily until we're properly implementing them in the set reconciliation algorithm. Details.
  • Upgrade all protocol major versions.

0.4.0

  • Require new environment variable OPTIMISM_RPC_HTTP_HOST
  • Website: Minor stylistic changes
  • Protocol:
    • All semantic version identifiers have been upgraded as a major version to avoid name collisions with nodes running older versions of this software.
  • API
    • POST /api/v1/list endpoint now adds two new properties to a message identity and signer.
    • POST /api/v1/messages now accepts messages signed from a delegate address.
  • Docs
    • Document difference in ports between API and frontend

0.3.0

  • Environment Variables
    • Rename TIMESTAMP_TOLERANCE_SECS to MAX_TIMESTAMP_DELTA_SECS
    • Separate HTTP_PORT (for website) and API_PORT (for node API)
    • Remove all TODAYS_EDITOR_... variables
  • Add Sphinx docs
  • Many changes to the frontend (won't list details)
  • Separated website frontend from node API frontend. They now run on differnet ports.
  • The allowlist registry now guarantees returning a set of unique Ethereum addresses.
  • The level's remote comparisons are now validated at the receiving node with a JSON schema.
  • For the frontend (mainly), ecrecover can now cache recovering signatures.
  • Logging of errors in the reconciliation algorithm has been improved.
  • The "leaves" function can now be called with a "startDatetime" parameter to speed up database queries.
  • Database migration
    • This release contains an incompatible database migration from earlier releases! If you actually need to migrate from 0.2.0 to 0.3.0, contact @timdaub!
    • LMDB now uses "ordered-binary" key encoding to enable range based key queries.
    • Our message hashing and identity generation was broken and so messages weren't canonically hashed. This had to be addressed in a database migration and by fixing the canonicalization. Again, if you really must migrate your data, make sure to contact us beforehand!
    • We enabled "useNodePruning" in ethereumjs/trie (this gets rid of deleted nodes).
  • Protocols
    • Upgrade "leaves" protocol to v3.0.0
    • Improve PMT traversal algorithm that had a bug when branching factor was too big (vendored in WalkController from ethereumjs/trie).
    • Improved atomic committments of data entry within set reconciliation. Made both metadb and trie transactional.

0.2.0

  • We discovered a critical issue in the v0.1.0 code base that lead to digests of messages in the ordering algorithm to be non-canonical [1] (thanks @freeatnet).
  • We're fixing this using npm:canonicalize that implements RFC8785 for canonical JSON.
  • Since we already had nodes in the network with existing data we shipped our first migration too. It checks for the digest algorithm, and applies a migration accordingly. We strongly recommend doing a backup of the DATA_DIR before applying the v0.2.0 update.
  • To avoid synchronizations between v0.1.0 nodes and v0.2.0 nodes, we upgraded all protocol and pubsub identifiers and versions too to avoid corrupting data bases.

1: https://github.com/attestate/kiwistand/commit/debb66ab676053a82b9aab789f68049a5c9a4528

0.1.0

  • Initial release of Kiwi News run until 2023-05-06