* Open dashboard app tiles in a new tab at the app's index_url Clicking an app tile in the dashboard now opens the app in a new browser tab at its index_url, with the same query params (puter.app_instance_id, puter.app.*, puter.auth.token, puter.domain, puter.api_origin, puter.locale, puter.args, ...) its in-page iframe gets. launch_app takes a newTab option: it runs the normal launch (private access, file signing, token) and sends the caller's tab to the built URL instead of opening a window. The tile click opens the blank tab inside the click so popup blockers allow it, and closes it if the launch fails. * Hand an app tab its token through the opener, not the URL An app opened in its own tab no longer gets puter.auth.token in its URL, which kept the token in browser history, and which puter.js ignored anyway outside an iframe. The GUI now holds the token for the tab it opened. Once the app loads, puter.js (standalone, with puter.app_instance_id in its URL and an opener) asks its opener for it at the configured GUI origin, takes only a reply from that window at that origin, and then clears window.opener. The GUI answers once, only to that tab at the origin it sent the tab to, and drops the offer after two minutes. Sign-in and implicit auth wait for the handoff, then fall back to the normal sign-in flow when no token came (opener closed, slow load, redirect to another origin). Built-in apps join explorer in opening in-page, since they belong to the desktop. * Open files from the dashboard Files tab in a new tab Opening a file in the Files tab (double-click, Enter, tap, or the Open menu item) now opens its app in a new browser tab, the same way app tiles do. The tab is opened inside the gesture and handed to open_item, which passes it to launch_app or closes it when the file opens no app. Web links reuse the tab, cut off from the dashboard. A second file of a multi-select Enter, whose tab the popup blocker refuses, opens in-page. The opened file's signed read/write URLs don't expire, so a tab gets them through the opener handoff with its token instead of in its URL; the URL keeps only puter.item.uid as a marker. puter.js delivers the handed-over item to onLaunchedWithItems / onItemsOpened, and wasLaunchedWithItems answers true while it's on its way. launch_app now closes a tab it was given whenever the launch fails, so callers don't each have to. * Open "Open With" apps from the Files tab in a new tab Picking an app under Open With in the dashboard Files tab now opens it in a new browser tab, like opening the row. The tab is opened in the menu click, or, when the "always open with" prompt comes first, right after it's answered, since that click counts as a fresh user gesture. If the popup blocker refuses the tab, the app opens in-page. The desktop keeps opening in-page. * Wait for a GUI-opened tab's token in getUser, whoami and getProfile A tab the GUI opens for an app gets its token over postMessage moments after puter.js loads. These calls short-circuited on a missing token, so an app reading the user at startup failed with a 401 (or a null profile) even though the token was on its way. They now wait for the handoff first, the same way sign-in already does. * Close an app's blank tab before alerting that its launch failed A launch denied for a private app, or one whose token couldn't be fetched, awaited its alert while the blank tab it was given stayed in front. The alert renders on the dashboard behind it, so the user was left on an empty tab until they went looking. The tab is now closed before the alert shows. * Launch an app tile in-page when the browser refuses its tab When window.open returned null (a popup-blocking setting, or a context that can't open tabs), clicking an app tile did nothing at all. It now falls back to the in-page launch it had before, as opening a file from the Files tab and Open With already do: an open window is surfaced, otherwise the app opens maximized with the tile's open morph. * Open no tab for a file opened from the Trash Opening a file in the Trash only explains that it can't be opened, but the Files tab still opened a blank tab for it first, which then closed again: a flash of a new tab before the alert. Trashed files (by the path a shortcut resolves to) now get no tab. * Don't count a tab launch as one that can report interactivity launch.ipc_tracked on app-is-ready is the denominator for the app-interactive series: launches that could produce one. An app opened in a new tab never signals over IPC, yet was recorded as tracked, so every tab launch counted as an interactive span that never came. Tab launches are now recorded as untracked. * Leave openerLaunch_ unset when there is no GUI opener to ask requestLaunchFromOpener resolved null asynchronously even when there was no opener at all, so openerLaunch_ was briefly set on every boot with an app instance id in the URL. A reloaded app tab (opener already cut off, URL still marking the opened item with puter.item.uid) reported wasLaunchedWithItems() as true to code running in the same task as the boot, though no item would ever arrive. With no opener to ask it now returns null synchronously and nothing is left pending. * Call an app tab launch off when the user closed its tab Closing the blank tab while the launch's fetches were in flight didn't stop the launch: it still navigated the closed tab, offered it a token, and recorded the app as opened (recent apps and /rao). The launch now checks the tab before sending the app there and ends as not launched, without an alert, when the user has closed it. * Keep a same-origin opener instead of asking it for a GUI launch Any top-level page with an app instance id and an opener asked that opener for its launch, waited out the timeout when no answer came, and then nulled window.opener. A page an app opened from its own origin (a pop-out of itself, say) can't have the GUI as its opener, yet it waited five seconds before its launch item and sign-in, and lost its link to the app window that opened it. An opener readable from this page, on an origin other than the GUI's, is now left alone and not asked.
The Open-Source Internet Computer!
« LIVE DEMO »
Puter.com
·
App Store
·
AI Builder
·
Developers
·
X
Puter
Puter is an advanced, open-source, self-hostable internet computer designed to be feature-rich, fast, and highly extensible.
For Users
Puter's goal is to provide you with every app and feature you need to work, create, and play under one roof. From a simple Notepad and Voice Recorder to Spreadsheet and Camera, Puter wants to be the all-in-one solution for your digital life.
For Developers
Puter provides everything you need to build and publish web apps and games. From AI to Cloud Storage and Database to Serverless Workers, Puter has you covered. Puter also helps you get users! Once you build your app, you can publish it on our App Store to reach and monetize users.
Getting Started
💻 Local Development
git clone https://github.com/HeyPuter/puter
cd puter
npm install
npm start
→ This should launch Puter at http://puter.localhost:4100
To run this checkout with Docker, follow Building from source. Create a local docker-compose.override.yml to select the local build; keeping these settings out of docker-compose.yml avoids conflicts when pulling updates and keeps local configuration out of pull requests.
🚀 Self-Hosting
Linux/macOS
curl -fsSL https://puter.com/selfhost | sh
Windows
irm https://puter.com/selfhost?os=windows | iex
→ For more details, see Self-Hosting Puter.
☁️ Puter.com
Puter is available as a hosted service at puter.com.
Support
Connect with the maintainers and community through these channels:
- Bug report or feature request? Please open an issue.
- X (Twitter): x.com/HeyPuter
- Security issues or abuse reports? security@puter.com
- Email maintainers at hi@puter.com
We are always happy to help you with any questions you may have. Don't hesitate to ask!
License
This repository, including all its contents, sub-projects, modules, and components, is licensed under AGPL-3.0 unless explicitly stated otherwise. Third-party libraries included in this repository may be subject to their own licenses.
Translations
- Arabic / العربية
- Armenian / Հայերեն
- Bengali / বাংলা
- Chinese / 中文
- Danish / Dansk
- English
- Farsi / فارسی
- Finnish / Suomi
- French / Français
- German / Deutsch
- Hebrew/ עברית
- Hindi / हिंदी
- Hungarian / Magyar
- Indonesian / Bahasa Indonesia
- Italian / Italiano
- Japanese / 日本語
- Korean / 한국어
- Malay / Bahasa Malaysia
- Malayalam / മലയാളം
- Dutch / Nederlands
- Polish / Polski
- Odia / ଓଡ଼ିଆ
- Portuguese / Português
- Punjabi / ਪੰਜਾਬੀ
- Romanian / Română
- Russian / Русский
- Spanish / Español
- Swedish / Svenska
- Tamil / தமிழ்
- Telugu / తెలుగు
- Thai / ไทย
- Turkish / Türkçe
- Ukrainian / Українська
- Urdu / اردو
- Vietnamese / Tiếng Việt

