๐ŸŒ€ Introducing SteemTwist โ€” Steem with a Twist

in Steem Dev โ€ข 2 days ago (edited)

1000096812.jpg

Hello Steem Dev community! ๐Ÿ‘‹

I'm excited to share a project I've been working on: SteemTwist, a decentralised microblogging dApp built entirely on the Steem blockchain. No backend, no build tools, no server โ€” just four static files and the chain.

๐Ÿ”— Live app: https://puncakbukit.github.io/steemtwist
๐Ÿ“ฆ Source code: https://github.com/puncakbukit/steemtwist
๐Ÿ“œ White Paper
https://github.com/puncakbukit/steemtwist/blob/main/SteemTwist_WhitePaper.pdf

What is SteemTwist?

SteemTwist is a Twitter/X-style microblogging experience powered by Steem. Every post, reply, vote, follow, and encrypted message is a native Steem blockchain operation. Content is permanent, censorship-resistant, and owned entirely by the author's Steem key โ€” not by any company or service.

A SteemTwist user is called a Twister ๐ŸŒ€. Short posts are Twists, replies are Thread Replies, upvotes are Twist Love โค๏ธ, resteems are Retwists ๐Ÿ”, and notifications are Signals ๐Ÿ””.

Tech Stack

The entire app runs in the browser from four files:

FileRole
index.htmlHTML shell with CDN scripts (SRI-verified) and CSS tokens
blockchain.jsAll Steem API and Keychain helpers โ€” no Vue dependency
components.jsVue 3 components compiled at runtime from CDN
app.jsVue Router views, routes, and global state

Dependencies (all CDN, all SRI-verified):

Hosting is on GitHub Pages. Because Vue Router uses createWebHashHistory, all routes work with zero server configuration.

How Data is Stored

SteemTwist uses a thin convention on top of standard Steem comment operations. At the start of each month, the @steemtwist account publishes two root posts:

feed-YYYY-MM    โ† all Twists are direct replies to this
secret-YYYY-MM  โ† all encrypted Secret Twists are posted here

Twists have permlinks in the format tw-YYYYMMDD-HHMMSS-username. The full tree looks like this:

@steemtwist/feed-2026-03
โ”œโ”€โ”€ @alice/tw-20260315-091530-alice     โ† Twist
โ”œโ”€โ”€ @bob/tw-20260315-102244-bob         โ† Live Twist โšก
โ”‚   โ”œโ”€โ”€ @alice/tw-20260315-150012-alice โ† Thread Reply
โ”‚   โ””โ”€โ”€ @carol/tw-20260315-160000-carol โ† Flag reply
โ””โ”€โ”€ ...

All Twists are broadcast with max_accepted_payout: "0.000 SBD" and allow_curation_rewards: false. Twist Love works as social appreciation without moving money.

Features

๐Ÿ  Feed & Navigation

  • Home โ€” personalised stream from Twisters you follow, with Firehose ๐Ÿ”ฅ real-time streaming (filtered to followed users only)
  • Explore โ€” global Twist Stream with New / Hot / Top sort modes and a live Firehose
  • Understream ๐ŸŒŠ โ€” a toggle on every page that widens the view from SteemTwist-only content to the full Steem blockchain (all posts, full account history)
  • Profile โ€” paginated Twist history with a pinned Twist slot, Understream support, and a refresh button

๐ŸŒ€ Twists

  • Up to 280 characters with full Markdown and a real-time Write / Preview tab
  • Edit โ€” re-broadcasts the comment op; the card updates instantly in the feed
  • Delete โ€” uses delete_comment if the post has no votes or replies; falls back to blanking the body otherwise
  • Pin โ€” pin one Twist to the top of your profile via an on-chain custom_json operation
  • Retwist โ€” resteem any Twist
  • Twist Love โ€” upvote at 100% weight

๐Ÿ‘ค Social

  • Rich profile cards with avatar, reputation (1โ€“100 scale), bio, location, website (https-only validation), join date, and stats
  • Paginated Followers / Following / Friends (mutual follows) pages with Follow/Unfollow buttons per row
  • Follow and Unfollow using Steem's native follow plugin

๐Ÿ”” Signals (Notifications)

Scans the latest 500 account-history entries and classifies each into one of six types:

SignalTrigger
Twist Love โค๏ธIncoming upvote on your Twist
Reply ๐Ÿ’ฌNew Thread Reply on your Twist
Mention ๐Ÿ“ขYour @username appears in a Twist
Follow ๐Ÿ‘คSomeone followed you
Retwist ๐Ÿ”Someone resteemed your Twist
Secret Twist ๐Ÿ”’An incoming encrypted message

Unread signals show a badge count in the nav. The badge clears when you visit the Signals page.

1000096813.jpg

โšก Live Twists โ€” The Unique Feature

This is the part I'm most excited about. A Live Twist is a Steem comment whose json_metadata carries a JavaScript code payload alongside type: "live_twist". When a viewer opens the Twist, they see a โ–ถ Run button. Clicking it executes the code inside a sandboxed iframe and renders the output inline in the feed.

Live Twists turn static posts into interactive widgets โ€” polls, calculators, charts, blockchain dashboards, animated greeting cards, games โ€” all stored permanently on-chain.

On-Chain Format

{
  "type": "live_twist",
  "version": 1,
  "title": "Click Counter",
  "code": "let n=0; function draw(){ app.render('<button id=b>Clicks: '+n+'</button>'); document.getElementById('b').onclick=()=>{n++;draw();}; } draw();"
}

The body of the Steem comment defaults to "โšก Live Twist โ€” view on SteemTwist" so it renders gracefully on Steemit and other clients.

Sandbox API

Author code communicates with the host page through an app object:

MethodDescription
app.render(html)DOMPurify-sanitise and inject HTML; auto-resizes the iframe
app.text(str)Set body as plain text (max 2,000 characters)
app.query(type, params)Call a read-only Steem API method
app.ask(type, params)Like app.query() but returns a Promise โ€” safe for concurrent calls
app.action(type, params)Request a Keychain-signed blockchain operation with a confirmation modal
app.log(...args)Append to the built-in console panel

app.ask() uses per-request IDs so concurrent queries never collide:

const [ticker, props] = await Promise.all([
  app.ask("getTicker", {}),
  app.ask("getDynamicGlobalProperties", {})
]);
app.render("<b>STEEM:</b> $" + parseFloat(ticker.latest).toFixed(4) +
           "<br><b>Block:</b> #" + props.head_block_number);

Over 70 read-only Steem API methods are available via app.query() / app.ask(), and 10 Keychain-signed action types are available via app.action() (vote, reply, follow, transfer, delegate, power up/down, vote witness, retwist).

40-Template Gallery

The composer ships with 40 ready-to-use templates in four tabs:

TabTemplates
SimplePoll, Quiz, Clicker, Calculator, Chart, Expandable, Story, Demo, Explorer, Prototype
GreetingsBirthday, New Year, Congratulations, Wedding, Graduation, Eid, Christmas, Thank You, Get Well, Anniversary
QueriesAccount Info, Trending Tags, STEEM Price, Hot Posts, Follower Count, Top Witnesses, Chain Stats, Post Viewer, Order Book, Reward Pool
ActionsVote on a Post, Reply, Follow/Unfollow, Transfer STEEM/SBD, Delegate SP, Power Up, Vote for Witness, Retwist, Query then Vote, Query then Follow

Security

Live Twists are isolated by ten layered controls:

  1. sandbox="allow-scripts" only โ€” null origin, no same-origin access, no top navigation
  2. fetch, XMLHttpRequest, WebSocket, and window.open all throw inside the sandbox
  3. DOMPurify sanitises every app.render() call using a shared config identical in both viewer and composer preview
  4. 10 KB code size limit enforced before publish and before โ–ถ Run
  5. User must click โ–ถ Run โ€” never auto-executed on page load
  6. Keychain is unreachable from the sandbox โ€” all app.action() calls route through the parent page
  7. Each app.action() shows a confirmation modal with HTML-escaped parameters before touching Keychain
  8. Parent validates e.origin === "null" and e.source === iframe.contentWindow on every inbound postMessage
  9. All app.query() parameters are sanitised: strings capped to 256 chars, limits clamped to 1โ€“100
  10. All app.action() parameters are validated: username regex, decimal-only amounts, currency/unit allowlists

1000096814.jpg

Live Twist Flag System ๐Ÿšฉ

Users can flag a Live Twist authored by someone else. Flagging is a two-step Keychain sequence:

  1. A โˆ’10000 weight downvote via requestVote
  2. A reply comment via requestBroadcast with the reason stored in json_metadata

The two-step design is required because Keychain rejects vote ops bundled in requestBroadcast. The flag reason is one of 12 JS-specific categories:

ReasonDescription
๐Ÿช Session HijackingStealing session IDs via document.cookie
๐Ÿ’ณ Web Skimming / FormjackingIntercepting card numbers at form submission
๐Ÿ—„๏ธ Storage TheftReading tokens from localStorage / sessionStorage
๐Ÿ’‰ DOM-type XSSExecuting malicious scripts via URL parameters
๐ŸŽฃ Phishing Form InsertionInjecting fake login forms into legitimate pages
๐Ÿช„ UI RedressingTricking users into clicking hidden/overlaid elements
โ›๏ธ CryptojackingBackground CPU mining while the page is open
๐Ÿ” Browser FingerprintingTracking users without cookies via JS-collected data
๐Ÿ“ Sensor / Location AbuseDeceptive permission prompts for device sensors
๐Ÿ› ๏ธ Client-Side Logic TamperingBypassing JS-based admin checks via dev tools
โ†ฉ๏ธ CSRFSending unintended requests to third-party sites
โš ๏ธ OtherAny other harmful behaviour not listed above

๐Ÿ”’ Secret Twists โ€” End-to-End Encrypted Messaging

Secret Twists provide private messaging between Steem accounts using the memo key pair. Encryption and decryption are delegated entirely to Steem Keychain โ€” plaintext never touches SteemTwist's code.

Sender โ†’ requestEncodeMessage (Keychain) โ†’ broadcast to @steemtwist/secret-YYYY-MM
Recipient sees ๐Ÿ”’ signal โ†’ requestVerifyKey (Keychain) โ†’ message revealed
  • Unlimited message length with full Markdown support
  • Nested encrypted replies โ€” each decrypted individually on demand
  • Only the recipient (not the original sender) can reply
  • Invisible in the regular Twist feed โ€” only appears in the Secret Twists inbox

RPC Fallback Nodes

All blockchain calls use automatic node rotation on failure:

  1. https://api.steemit.com
  2. https://api.justyy.com
  3. https://steemd.steemworld.org
  4. https://api.steem.fans

Forking / Deploying Your Own Instance

The TWIST_CONFIG object in blockchain.js centralises all deployment-specific constants:

const TWIST_CONFIG = {
  ROOT_ACCOUNT:       "steemtwist",
  ROOT_PREFIX:        "feed-",
  SECRET_ROOT_PREFIX: "secret-",
  TAG:                "steemtwist",
  POST_PREFIX:        "tw",
  DAPP_URL:           "https://puncakbukit.github.io/steemtwist"
};

To run your own independent instance: update these values, push the four files to any static host, and publish your own monthly root posts. No server configuration is needed.

What's Next

This is version 0.1 โ€” a solid foundation, but there's plenty of room to grow. I'd love to hear feedback from the Steem Dev community on:

  • The Live Twist sandbox API โ€” are there read-only query types you'd find useful that aren't currently supported?
  • The data model โ€” any thoughts on the monthly root post approach vs. alternatives?
  • Security โ€” any concerns or suggestions about the sandbox design?

Feel free to try it out, spin up your own fork, or open issues on GitHub. All contributions are welcome.

Thanks for reading! ๐ŸŒ€


Built with steem-js ยท Steem Keychain ยท Vue 3 ยท MIT License

Assisted by:

See also:

Sort: ย 

Nice..thank you very much for your good efforts in making steem more amazing โค๏ธ

You're welcome

Upvoted! Thank you for supporting witness @jswit.

ย 9 hours agoย 

Looks Interesting , Also have you tried #blazedit


This content was created on #BlazedIt and first appeared on https://alpha.blazedit.xyz .

Coin Marketplace

STEEM 0.06
TRX 0.32
JST 0.059
BTC 67618.34
ETH 2061.89
USDT 1.00
SBD 0.50