FrameOS
Guide

FrameOS Cloud

Manage frames from cloud.frameos.net without running a backend - enroll a Pi or ESP32 in minutes, assign scenes from the store, see logs and previews. Free for a normal home.

FrameOS Cloud is the hosted way to run FrameOS. You create an account, point a frame at it, and manage that frame from a browser: assign scenes from the scene store, change settings, watch logs and metrics, queue firmware updates. There is no backend to install and nothing to keep running at home.

It is one of three ways to run FrameOS, next to a standalone frame and the self-hosted backend. Pick it when you don't want to host anything, or when your frames live somewhere your laptop isn't. Nothing about it is required: a frame set up through the cloud keeps rendering its scenes if the cloud is unreachable, and can be moved back to standalone or self-hosted operation at any time.

Beta

FrameOS Cloud is new and still changing. It works - frames enroll, scenes deploy - but expect rough edges. The free plan costs nothing, with fixed limits (frames, storage, logs) so one account cannot crowd out the rest; paid plans raise them. Your data stays yours: scenes, backups and frame settings can be exported from your account at any time. Found a bug? Report it on GitHub or Discord.

What the cloud does, and deliberately doesn't

The cloud is identity, store, relay and fleet view. The frame is the product. Everything still renders on the device, from scenes stored on the device, on the device's own schedule.

A cloud-managed frame is also deliberately limited. The cloud:

  • never gets a shell. There is no SSH, no terminal, no remote command execution. The protocol between frame and cloud has no verbs for it.
  • never compiles code. Only sandboxed, interpreted scenes can be pushed - the same JavaScript-and-node-graph scenes that run on an ESP32. Scenes that run shell commands are refused.
  • never proxies your images. Frames fetch images straight from the source and decode them on the device, whatever their size.
  • never renders for you. Previews are the frame's own last render, or rendered in your browser.
  • only sees logs and metrics if you switch that on, per frame.
  • never sees your WiFi password. An SD image built in the cloud is assembled in your browser: the WiFi credentials and root password are written into the image locally and never sent to the server. The same goes for an ESP32 flashed from the browser - WiFi goes straight to the board over USB. The cloud gets the frame name, the display and a claim token, nothing more.
  • never holds your backups in the clear. Backups from a linked backend are encrypted before upload with a key only you have; the cloud stores ciphertext.
  • ships no binaries. Firmware updates are a nudge: the frame downloads the release itself and verifies its signature before installing.

The local escape hatches stay open. A cloud-managed Pi still serves its control page and HTTP API on port 8787, and its admin page is where you disconnect it from the cloud if you ever want to. If a frame loses its cloud link it simply becomes a standalone frame, still showing the scenes it had.

The cloud itself is open source (AGPL-3.0, in the cloud/ folder of the main repository) and the enrollment protocol is documented so anyone can reimplement it. Running your own copy is possible but not recommended yet - it is built for exactly one deployment.

Create an account

Sign up at cloud.frameos.net/signup with an email address and a password, or Continue with Google. Verify your email and sign in; you land on Home: your most recent frames, the latest additions to the scene store, your private scenes and photo albums, and your usage at a glance.

The rail down the left holds everything else: Frames (your fleet), Scenes (the scene store, your own scenes under My cloud scenes, and every frame's Installed scenes), Apps, Photos and Settings. The settings pages live at /account/<page>: Backends (linked self-hosted backends), Service keys, SSH keys, Usage (frames, storage, photos and AI), Backups, Activity (a searchable feed of everything that happened on the account), Security and Developer (API tokens and the MCP server), with Sign out at the bottom of the panel. Under Security you can add a passkey or TOTP two-factor authentication - recommended for an account that controls physical frames.

Add a frame

With no frames yet, the Frames page is the Add a frame panel; later it's the Add frame button. There are four ways in, matching the hardware you have:

SD card image (Raspberry Pi)

The cloud builds a personalized SD image in your browser:

  1. Pick your board: raspberry-pi-64 (Zero 2 W, Pi 3, Pi 4), raspberry-pi-5 (Pi 5, CM5) or raspberry-pi-32 (Zero, Zero W, Pi 1).
  2. Name the frame, pick its display (or choose to pick it later from the frame's setup hotspot), and enter your WiFi network. A Country field, prefilled from your browser's locale, sets the radio's regulatory domain - without it a frame can't join access points on 2.4 GHz channels 12 and 13. Optionally set a root password and tick the SSH keys that should be able to log in as root - add one right there or under Settings → SSH keys; the cloud keeps public keys only, and the builder shows how much of the card's 4 KB configuration area they take. Your browser's time zone is written into the card too, and for HDMI the panel size is autodetected - only displays with no size in the catalog need a width and height.
  3. Click Download SD image.

Your browser downloads the generic release image, verifies its release signature, writes your settings into its boot partition, and hands you a .img.gz. WiFi credentials and the root password never leave your browser - only the frame name, display and a claim token travel to the cloud. Flash the image with Raspberry Pi Imager ("Use custom image") or balenaEtcher, insert it, and power up.

On first boot the Pi joins your WiFi (or raises its FrameOS-XXXX setup hotspot if it can't), takes its hostname from the frame name (slugified: "Kitchen Frame" becomes kitchen-frame.local), and enrolls. The panel shows the FrameOS status screen - name, resolution, network, Managed via: FrameOS Cloud - until you give it a scene.

You can flash one image to as many cards as you like. Every frame that enrolls from the card appears as pending - the first one included: open it under Frames and click Confirm frame in the banner at the top of its page. Until you do, the frame accepts no scenes or settings - so a lost SD card can't quietly add frames to your account. By default the image keeps working forever; tick Stop this SD card from adding frames after a while on the builder to give its claim code an expiry.

Install script (any Pi, most Linux)

Already have a Pi running Raspberry Pi OS, or some other Debian-flavoured box? Copy the one-liner from the Install script tile and run it on the device:

curl -fsSL https://cloud.frameos.net/install.sh -o /tmp/frameos-install.sh && \
  sudo FRAMEOS_CLAIM_TOKEN=FRCT_... sh /tmp/frameos-install.sh

It asks for the display, time zone and an admin password, installs prebuilt FrameOS binaries, and enrolls the frame on first start. The claim token is single-use and expires after 24 hours; the panel mints a fresh one every time you open it.

A frame that is already up - flashed from a generic image, say - can be claimed without reflashing:

  • On the frame's admin page, open Settings → FrameOS Cloud → Connect to cloud.frameos.net. The frame shows a short code like H7LU-JLWN. While the link is pending, the same code and a QR code are drawn on the panel itself - over the current scene, or beside the rows on the status screen.
  • Scan the QR, or open Add frame → Link a frame that already runs in the cloud, type the code from the frame, check what is asking (the frame introduces itself by its hostname, kind and address) and approve. The drawer opens the frame the moment it has enrolled. cloud.frameos.net/device still works too.

Afterwards the frame's FrameOS Cloud box shows a Cloud status - Linked, not managed or Managed from the cloud - and its switches: Ship logs and metrics to the cloud, Manage this frame from FrameOS Cloud and Sign in here with FrameOS Cloud. The admin password sits under the last one, and the two logins can never both be off. Switching management off and on again re-registers the same frame, not a second one.

A frame that already ran scenes of its own when it joined - a standalone Pi, say - offers them in a banner on its page (Linux frames): Import scenes turns them into private scenes in your account and sends them back to the frame, each keeping its state. The frame's own copies are left alone, and none of your service keys are shared with the imported scenes until you allow them.

Seeing the code on the display is the proof of ownership, so a frame linked this way is active right away. In the frame's setup hotspot you can also pick Manage via: FrameOS Cloud and leave the claim code empty - the frame shows a link code on its display as soon as it is online.

Flash an ESP32 from the browser

Plug an ESP32-S3 board in over USB, pick the hardware preset (Waveshare PhotoPainter, TRMNL DIY kit, Seeed reTerminal, CrowPanel…), name the frame, enter WiFi and click flash. The browser writes the generic release firmware with Web Serial (Chrome or Edge), then provisions the cloud address, a claim token and your browser's time zone over the serial console. Either port a board offers works - the chip's USB JTAG/serial debug unit or a USB Single Serial bridge such as the CH340 on the Seeed reTerminals (the flasher used to refuse those). A brand-new board formats its storage on first boot - about four minutes on a 32 MB board - so the flasher waits as long as the image's storage takes to format, gives up only after 90 seconds of silence, and tells you what it saw if the prompt never comes. It also refuses to write an S3 image onto a different chip.

ESP32 frames are full frames in the cloud too: scenes render on the chip, with logs, metrics, previews, schedules and signed over-the-air updates. PSRAM-less boards (ESP32-C3: the original TRMNL, XTEINK X4) would need the cloud to render every refresh for them, which it doesn't offer yet - their enrolment is refused. Run those from a self-hosted backend.

A virtual frame

On a paid plan, Add frame → Virtual frame makes a frame with no hardware at all: the cloud renders its scenes and shows the picture at a private link, for a browser on a spare screen, a tablet or a TV. See virtual frames.

Every path offers Start it with the scenes from… an existing frame, so a second kitchen frame can be a clone of the first. Scenes are copied; assets on the SD card are not. Every path also seeds the new frame's time zone from the browser you added it in.

Managing a frame

Each frame has an overview page at /frames/<id>, with its tools as sub-paths - Settings, Preview, Schedule, Logs, Metrics, Assets and Activity (/frames/<id>/logs) - and its scenes at /frames/<id>/scenes/<sceneId>.

  • Scenes. Install from the scene store, or open a scene in the built-in editor - the same node-graph editor the backend has - and save it. A scene's drawer has three buttons: the main one puts the scene on the frame as it stands - Push & activate while the frame doesn't have your latest edits, plain Activate once it does - Preview runs it in your browser with the values in the drawer, and Reset puts them back. A push sends only that scene and the scenes it draws or switches to. The frame applies it within seconds while connected; pushes to an offline or sleeping frame queue and land when it wakes - an hourglass says so. A frame holds up to 20 scenes. Every scene remembers where it came from - the drawer says Installed from ‹scene› · v3 - so you can tell a store scene from your own edit of it, and which version is on the frame. When the store has a newer version, the scene's image carries an Update available flag; click it, or Update to latest in the scene's … menu, and after one confirmation the frame moves to the newest version - order, pins and key grants are kept. The confirmation says what the new version may do that the old one couldn't (permissions), and Update all scenes takes every pending update in one push. A scene that asks for a service key the frame isn't allowed says so in its drawer - Uses your Unsplash key, not allowed on this frame - with an Allow button. Map buttons in the scene's menu says which of the frame's keys the scene's button listeners answer to (details), and a scene shown with Preview on frame or by the voice chat sits above the list as a preview row, with Install and Discard.
  • Preview. The frame's own last render, plus per-scene snapshots, fetched while you have the page open. An ESP32 frame sends its framebuffer on request; a sleeping one delivers it on its next wake, and its image wears a small moon badge until then. Fleet tiles can also render a scene in your browser on request, and each card has a re-render button.
  • Progress. Every push, firmware update, restart and reboot draws a progress bar on the frame's card, from the click to the first render. A firmware update reads updating firmware 42%, rebooting into 2026.10.19 or update failed with the device's reason, and Update all next to Active in the sidebar updates every frame that's behind.
  • Sleep forecast. A frame that deep-sleeps says so before it halts: the sidebar and the frame's page show asleep · wakes in 5 min (or overdue) rather than a stale "last seen", and battery frames show their charge in place of the status dot - click it for the charge, a two-week history and a forecast of the battery's life.
  • Settings. The set a cloud-managed frame accepts is declarative: name, rotation, render interval, scaling, time zone, debug logging, the QR control code, metrics interval, HTTP response cap, palette, colors and partial-refresh policy, GPIO buttons, local network access, and on ESP32 the power options - deep sleep, battery pins and the battery enable pin, how long it stays awake after a tap - the button and touch beep, and whether the frame's own screen offers its settings page. Also button actions, and on an ESP32 the status LED, the microphone's pins and the voice chat with its four permission switches. A Pi's Display driver can be switched too: the frame runs the new driver's setup and restarts or reboots by itself. An ESP32's GPIO buttons show as the board reports them, with Edit buttons to change them. Everything else about the device - WiFi, mount points, an ESP32's panel - is owned by the device. A rotation or scale someone changes on the frame's own screen shows up here too. A setting newer than the frame's firmware is shown disabled with the version it needs.
  • Schedule. Scene switches, events, and restart/reboot entries, evaluated on the frame in its own time zone.
  • Logs and metrics. Off by default for privacy: flip Ship logs and metrics to the cloud under the frame's Log shipping settings (or on the frame's own admin page). Logs keep the last 5000 lines per frame; metrics charts include reboot markers.
  • Service API keys. Enter OpenAI, Unsplash, Immich, Home Assistant and similar keys once under Settings → Service keys. Each frame has a Deliver keys to this frame switch, and only receives the keys its scenes actually ask for, pulled by the device over HTTPS.
  • Ask AI. The frames home and every frame's page have an AI box. Ask what's on the kitchen frame right now? or put a weather scene on the hallway frame: the chat answers from live data and acts with the same tools as the MCP server, asking before anything that changes a frame, and the frames and scenes it names are links. A scene it installs is rendered in the frame's own memory first, so an ESP32 isn't handed a scene it can't hold. The picture of a scene it made shows under the reply, and one that passes the check becomes the scene's image until the frame renders it. A reply can run to 40 rounds of tools; one that runs out says how far it got, so you can say continue. History keeps earlier chats.
  • Assets. Browse and upload files in the frame's assets folder (its SD card on an ESP32).
  • The ⋯ menu: rename, render, Render status screen (Linux frames), restart, reboot, Update firmware (shown as Update queued while the nudge waits for the frame), delete. Frames on an HDMI/framebuffer display or a HyperPixel also get Turn display off / Turn display on - the panel powers down while the frame keeps rendering, and scripts can send the same. Deleting a frame revokes its link; the device keeps rendering what it has, as a standalone frame.
  • Activity. An audit trail per frame: connections, pushes, firmware changes, renames.

Local network access

While cloud-managed, a frame blocks its scenes' HTTP requests to private addresses on your LAN - a Home Assistant at homeassistant.local, a NAS, anything at 192.168.x.x - so a scene installed from the store can't poke at your router. A blocked request fails with Local network access is off for this frame, and the Logs panel links to the setting.

To allow it, open the frame's settings in the cloud: Local network access → Turn on asks the frame to draw a six-digit code on its panel, and you type it into the same page. It takes a person in front of the frame: the code never leaves the device as an image, it's good for five minutes and five tries, and the frame takes at most ten wrong codes an hour. A sleeping frame shows the code when it wakes. Turning access off needs no code, but asks first, since turning it back on needs someone at the frame again. On an ESP32, set allow_local_network 1 on the USB console works too. Self-hosted and standalone frames have no such block - what they reach is your network's call. Devices a frame should never reach belong on a different network or VLAN either way.

Scripts and AI agents: API tokens and MCP

Everything the browser can do with your account is also available to scripts and AI agents, from Settings → Developer:

  • Personal API tokens (fc_api_…, or a read-only fc_apiro_…) stand in for your account on the JSON API. A token can do what you can do in the UI, minus the actions that always need a fresh sign-in in the browser (revoking frames, approving device links, changing your password). Minting one needs a recent sign-in too; tokens are shown once and stored hashed. Every token expires - after a year by default, which is also the longest - and all of them are revoked when you reset your password or add or remove a second factor. Sending a frame an event is one call with such a token.

  • A hosted MCP server at https://cloud.frameos.net/api/mcp - the same API with a vocabulary a Model Context Protocol client can read: around 80 tools across frames (list, rename, settings and schedules, assign and activate scenes, screenshots, logs, metrics, assets, reboot, firmware updates, claim tokens), scenes (read and save versions, create from JSON, zip or URL, fork, publish, gallery images, yank, delete, convert legacy compiled scenes), the store, your photo library (upload, albums, share links, install an album on a frame), a scene_guide with the rules for writing scene JSON, the scene AI, and a server-side render that runs the real FrameOS runtime on any scene JSON and hands the image back, plus the lint that publishing applies. Every tool that changes what a frame shows, stores or does takes confirm: true, so an agent only acts when you asked for it. Nothing local to run: it speaks Streamable HTTP with your token as a bearer.

    claude mcp add --transport http frameos https://cloud.frameos.net/api/mcp \
      --header "Authorization: Bearer fc_api_…"

    Claude Desktop, Cursor and other clients take the same thing as JSON:

    {
      "mcpServers": {
        "frameos": {
          "type": "http",
          "url": "https://cloud.frameos.net/api/mcp",
          "headers": { "Authorization": "Bearer fc_api_…" }
        }
      }
    }

    For clients that want a subprocess, the same server runs over stdio from a checkout of the repository: FRAMEOS_CLOUD_TOKEN=fc_api_… pnpm --filter @frameos-cloud/mcp start.

The MCP server is a thin wrapper over the cloud's routes (cloud/packages/mcp in the main repository), so anything it can do, curl with the same bearer can do too. A self-hosted backend has its own MCP server with the same vocabulary. For the frame's local API - no account involved - see Controlling the frame.

Linking a self-hosted backend

You can also keep a self-hosted backend and link it to your cloud account, for a few optional extras: signing in to the backend with your cloud account, end-to-end encrypted backups of your frames and scenes, and saving scenes to your private store. See Link the backend to FrameOS Cloud. A linked backend still deploys over SSH and still works with the cloud switched off.

Limits and plans

The free plan: 20 frames per account, 20 scenes per frame, 200 scenes and 100 MB of private scene storage per account, 100 MB of backups, 100 MB of logs, and 250 MB of photos. Settings → Usage shows where you stand, and under Need more? the plans: Free, Private ($2 to $29 a month, 40 to 160 frames) and Business ($49 to $499, 199 to 999 frames, with priority support). A slider sets the price, and $N a month is N times the free plan's storage plus N virtual frames. Sharing frames and SSO with SAML are planned for the paid plans and not part of them yet. A paid plan is bought by card from the same page and can be changed or cancelled there at any time: Your plan then leads it - when it renews, Card and billing details, and Cancel plan, which runs to the end of the month you paid for - and Invoices lists each one with its VAT and a PDF. The account, enrolling and managing frames, and installing public store scenes are meant to stay free; nothing security-related (two-factor auth, revocation, signed updates, the sandbox) will ever be paywalled. Pricing has the full table and the reasoning.

Privacy

The cloud stores your account, the frames you linked (name, hardware, version), the scenes and settings you give them, and - only if enabled per frame - logs and metrics. Backups from linked backends are encrypted in the backend before upload, with a key the cloud never sees. Data lives in the EU. Every sign-in, device link and permission change is listed under Settings → Activity. Photos are scanned against known abuse material before they're stored, reach frames only through share links you can withdraw, and leave the storage bucket when you delete them. Download my data under Security exports everything as JSON, and Delete my account is immediate and needs no support ticket. The full details are in the privacy policy.

Moving between cloud, backend and standalone

A frame is managed by exactly one of them at a time, and switching is always a local action on the device:

  • Cloud → standalone: delete the frame in the cloud, or Disconnect on the frame's admin page. The frame keeps its scenes.

  • Standalone → cloud: link it with the code on its display.

  • Standalone → backend: Adopt frame in the backend imports its settings and scenes - see First deploy.

  • Backend → cloud and back: disconnect from one side, then enroll or adopt from the other. A guided migration is planned but not built yet.

  • A cloud ESP32 with a backend set on it too (set backend on its console, a bench setup) stays enrolled, but takes its content from that backend and turns down the cloud's scenes, settings and schedule. The frame's page says which backend holds it. To hand it back, follow the banner to the USB view, connect the board and choose Hand back to the cloud, or on its console:

    frameos> set backend ""
    frameos> set api_key ""
    frameos> set frame_id 0
    frameos> restart

    The next time it connects it gets the cloud's settings, schedule and scenes again.

On this page