Bluesky Mini-Apps concept

@haileys.quest

I'm toying with an idea for lightweight Web pages you can embed into posts, and for now I'm calling them mini-apps. It will:

  • Primarily be implemented as a record of type app.bsky.feed.embeddedApp (NSID subject to change)
  • Use a app.bsky.embed.embeddedApp post embed, strong-ref to an embeddedApp record (NSID subject to change)
  • The embeddedApp record will point to a blob housing the actual HTML
  • The record will also have metadata about the app (see example record below)
  • You will be able to create posts using a mini-app, if the embeddedApp has a creation blob specified
    • The best way to do this is find the mini-app you want to create, open its dots menu, and press "Add to favorites", which adds it to a list (this may be saved to the PDS). Then when you go to make a new post, you can find it under one of the buttons on the bottom.
    • Favorite mini-apps will also show up somewhere else and you can pin them... somewhere (sidebar? menu?)
    • There might also be a search function for this, perhaps even in the composer itself.
  • You will be able to "trust mini-app publisher" in the dots menu of an account (@bsky.app will have this on by default), and "trust this mini-app" in the dots menu for an individual mini-app
    • Trusted publishers should be hidden (i.e. using Bluesky properties or app storage to remember them), but trusted apps could be saved to the PDS
    • Trusted mini-apps and all mini-apps by trusted publishers will skip the "start" screen, and they can hide the publisher for a cleaner look if the app record says to do that
  • When embedded in a post, if the mini-app is by the same person who embedded it, the publisher information is hidden by default
  • You can block individual mini-apps, which will show the embed the same as if it's a quote-post of someone you blocked
  • Be able to open in an "expanded view", which on desktop and tablets will take up most of the screen, with a dim background and transparent toolbar at the top (exit button, title, publisher, spacer, menu), and on phones will take up most of the screen with an opaque toolbar.
    • Post creation always shows in the expanded view.
    • On desktop, clicking the lightbox area beside (or below?) the mini-app closes it, same as an expanded image.
  • Show a basic, configurable "preview" by default, unless it's trusted (see above)
    • This is a security and performance thing, so you don't get random iframes loading while you scroll.
  • All mini-apps you've uploaded will show up in your profile, but they're unlisted by default so only you can see them. There will be a metadata field in the record you can set to "publish" it, making it visible in your profile and maybe in searches.
  • Maybe: in the app.bsky.actor.profile/self record, a new field could be added referencing a mini-app to show upon opening someone's profile. Mostly this is for bots and services to allow quick and easy access to configuring them, but you could also set it on regular users as well. It'll show a preview centered in its own first tab (similar to the Labels tab on a labeling service, which would still override this).
  • A full ATProto API client would be made available, but extremely limited. No account-level actions can be done other than identifying the user, and it can only write to records under the publisher's handle domain, i.e. a mini-app by @bsky.app would be able to write to records in the app.bsky.anything namespace, but not under the com.example.anything namespace (and it can be any level). Anything publicly readable is still readable by this client.
    • There should also be a re-authorizing flow between Bluesky and the mini-app, similar to OAuth but simply telling Bluesky to upgrade the client it gives the mini-app. This will back out of the expanded view and show something similar to the OAuth client authorizing screen, but in a Bluesky app dialog. (Exiting this dialog via authorizing or denying the expanded access goes back into the expanded view if it was in it before.)

Collection NSIDs a given mini-app is capable of writing to without re-authorizing:

  • The publisher's handle, and anything "under" it. (i.e. @bsky.app -> app.bsky.*)
  • Any NSID where Lexicon resolution points to that account. Usually this is the same as the handle but it may differ.
  • Maybe: any domain specifically authorizing the publisher to use it, via a TXT record in DNS (possibly an extension on top of _lexicon, i.e. a field app.bsky.feed.embeddedApp.allows=), which can be set to * to allow any mini-app to write to it (dangerous)

Example record

{
  "$type": "app.bsky.feed.embeddedApp",
  "title": "Poll", // REQUIRED; title shown in the expanded view, in lists, and on the preview
  "previewType": "poster", // optional, defaults to poster. (Probably a known-values string or open enum so that new types can be added, could also be a token)
  "aspectRatio": "4:3", // optional, defaults to 4:3; has known values, only used with "poster" preview type
  "alwaysOpenExpanded": false, // optional, defaults to false; used with previewTypes that don't always open expanded, such as poster
  "image": { // REQUIRED; the poster or icon
    "$type": "blob",
    "ref": {
      "$link": "bafkreibjfgx2gprinfvicegelk5kosd6y2frmqpqzwqkg7usac74l3t2v4",
    },
    "mimeType": "image/png",
    "size": 3482018
  },
  "publish": true, // optional, false by default
  "data": { // REQUIRED -- the most important field
    // yes I just reused the same blob from earlier
    // it's a syntax example so it doesn't matter
    "$type": "blob",
    "ref": {
      "$link": "bafkreibjfgx2gprinfvicegelk5kosd6y2frmqpqzwqkg7usac74l3t2v4",
    },
    "mimeType": "text/html",
    "size": 3482018
  },
  "creatorData": { // optional; if specified, anyone can create a post using this mini-app (good for polls)
    "$type": "blob",
    "ref": {
      "$link": "bafkreibjfgx2gprinfvicegelk5kosd6y2frmqpqzwqkg7usac74l3t2v4",
    },
    "mimeType": "text/html",
    "size": 3482018
  },
  "nsids": [ // optional; NSIDs for collections and XRPC endpoints that it can access
    "app.bsky.poll.answer"
  ]
}

Example embedding post

{
  "$type": "app.bsky.feed.post",
  "text": "",
  "embed": {
    "$type": "app.bsky.embed.embeddedApp",
    "record": {
      "cid": "bafyreif6m3ilqdx3i5a4lbfkaehri4i2a7ucmzgoqihpondhxw6ule7gmu",
      "uri": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.embeddedApp/poll"
    },
    "description": "When was the last time you drank water?", // optional; shows next to the title on the preview
    "alt": "A poll question: \"When was the last time you drank water?\"", // optional; might not actually keep this one
    "extraData": { // "unknown" type in Lexicon
      // you could put a $type here but it's not strictly necessary
      "question": "When was the last time you drank water?",
      "answers": [
        "Just now",
        "Less than an hour ago",
        "Some time today"
      ],
      "randomOrder": false
    },
  },
  "langs": [
    "en"
  ],
  "createdAt": "2025-07-31T17:24:58.405Z"
}

Use cases

  • Configuring services through Bluesky, such as self-labelers, bots, and bridges
  • Polls
  • Interactive maps
  • Games
  • Read longer posts or posts with enhanced syntax easily viewable from within Bluesky, such as Whitewind posts or posts through Bridgy Fed
  • RSVP to events (Smoke Signal)

Preview layouts

I should make mockups of these.

  • poster: like an image, but with a configurable "Start" button, and the title (and summary?) shown on top of it.
  • details: somewhat like a Bluesky List: an icon on the left, the title and summary shown in the middle, and on the right, a dots button and a colored Start button. This always opens in expanded view.
  • maybe more??? not too many though
haileys.quest
Hailey 💜

@haileys.quest

Trans rights are human rights, and the election was not stolen. 🏳️‍⚧️🚺

pfp is what I WANT to look like...

Post reaction in Bluesky

*To be shown as a reaction, include article link in the post or add link card

Reactions from everyone (0)