# Stashio — saving the internet without losing your mind > Save links and PDFs from the share sheet, find them later — Stashio’s capture and search flow, including when cloud helps. 2025-07-15 · 19 min read · stashio · by ntan (ntan) for uranashel Canonical HTML: https://uranashel.com/blog/stashio-second-brain.html --- Stashio is the odd app in the uranashel portfolio. Wheria, Estua, Sonarish and Phyzix treat the phone as a self-contained instrument: sensors in, processed numbers out, nothing leaves the device unless you export it yourself. Stashio breaks that pattern deliberately. The problem it solves is accumulation across time and devices. You find a PDF on your phone, a link on your laptop, a voice memo while walking, a screenshot of a parking pillar number at midnight (mine read E9, floor B2), and three weeks later you need "that article about barometer math" without remembering the exact title, the site or which app swallowed it. The article, for the record, was [this one](https://uranashel.com/blog/barometer-parking-math.html). The core job splits into three verbs: capture from anywhere, organize into named Spaces, retrieve by keyword or by meaning. Cloud sync sits on top as an optional enhancement. It never becomes the gate. ## Capture gets a 3-second budget The primary entry point is the OS share sheet, the system UI that appears when you tap Share in Safari, Chrome, a PDF viewer or a photo gallery. Every extra decision at that moment costs saves. So the design goal at capture time is zero friction: no mandatory tags, no folder picking, no "where should this go?" modal that makes you abandon the whole idea. You share, it stashes, you move on. On iOS that politeness has to survive a hostile runtime. Stashio registers a Share Extension, which the system runs as a separate lightweight process with a memory ceiling we have measured being enforced near 120 MB. The extension therefore does the minimum: it copies the shared item into an app group container, writes one metadata row and exits. Indexing waits until the main app next opens and gets signalled that new items are queued. On Android, Stashio registers as a Share Target in the manifest. Small items import inline. A large PDF triggers a foreground service so the system does not kill the import mid-write, and the extracted metadata lands in a Room database entry once the copy completes. From our July bench notes (3 iPhones, 3 Android phones, release builds, 200 shares each): median share-to-saved time was 1.9 s for a URL and 4.8 s for a 40 MB PDF. ## Extraction stays on the device Five input types are supported. URLs arrive with automatic title and preview extraction. PDF documents get on-device text extraction where the OS provides it, which covers most digitally-produced files. Images, including camera scans of receipts, run through the platform OCR path (Vision on iOS, the on-device ML Kit recognizer on Android); a typical receipt photo gives up its text in about 300 ms on an iPhone 12. Selected text fragments store as-is. Voice memos can be recorded inside the app or shared over from a recorder. Everything extracted feeds one local search index. None of it needs a network round trip, which matters when the thing being saved is a pillar screenshot three floors below the cell grid. ## Spaces are sets, files are singletons Organization happens after capture. Items live in Spaces: user-created collections like "Garage QA logs," "DSP papers," or "Recipe links". One item can belong to several Spaces at once without duplicating the underlying file. Internally a Space membership is a row in a join table; the blob exists exactly once on disk, reference-counted, so removing an item from its last Space is the only delete that ever touches storage. This sounds like database trivia until you watch someone file a manufacturer datasheet under both "DSP papers" and a project Space, then expect an annotation made in one place to appear in the other. It does, because there is only one item. Keyword search runs against titles, user-added tags and extracted text bodies, with prefix matching so "barom" surfaces the barometer material before you finish typing. ## Search by meaning, still offline Keywords fail in one specific way: you remember the concept but forget the vocabulary. For that case Stashio optionally computes an embedding vector for every item, on the device, and indexes the vectors locally. A query like "that thing about hydrostatic pressure in parking garages" can match a saved blog draft even if the word "hydrostatic" never appeared in your tags. The model is a small sentence encoder producing 384-dimensional vectors; similarity is plain cosine, `cos θ = (a·b)/(|a||b|)`, and at personal-library scale nothing cleverer is required. My 2,400-item library stores its full vector index in about 3.7 MB (2,400 × 384 floats × 4 bytes), and a brute-force scan over all of it finishes in under 2 ms on an iPhone 12. We benchmarked an approximate-nearest-neighbor structure anyway, then discarded it: at this scale ANN saved microseconds and charged an index rebuild on every edit. Ranking blends both signals: ``` // per query, all local kw_scores = bm25(query, titles + tags + bodies) q_vec = embed(query) // 384-dim, on-device sem_scores = {} for item in library: // 2,400 iterations, under 2 ms sem_scores[item] = cosine(q_vec, item.vec) final(item) = 0.6 * norm(kw_scores[item]) + 0.4 * norm(sem_scores[item]) return top_k(final, 20) ``` The 0.6/0.4 split came from ktuyen's relevance spreadsheet rather than from theory. She scored 60 test queries against both extremes and the blend, and pure semantic search kept ranking a receipt for waterproof boots above an actual fluid-dynamics paper. ## Why this app may touch the cloud Bookmarking does not need 10 ms latency. Nobody's sleep depends on a share extension finishing inside an audio buffer deadline; that world belongs to [the audio thread](https://uranashel.com/blog/dsp-audio-thread.html), where 10.7 ms is a hard wall. What a save-later tool does need is multi-device sync. Save on the phone, find on the tablet; that is the feature people actually pay for. So atuan built a small backend, with the same privacy instincts as the rest of the studio. The local cache is always readable offline. The account is optional. For embedding sync the server stores encrypted blobs, not plaintext content. Capture and search work without ever signing in, because the index is built on-device first and remains searchable in airplane mode. Our sensor apps run everything locally because physics demands it; Stashio keeps a cloud lane open because user workflow demands it. Same decision, opposite ends — the [on-device-first post](https://uranashel.com/blog/on-device-first.html) walks the whole spectrum. ## The 2,400-link stress test Before Stashio existed, my Apple Notes contained approximately 2,400 unsorted links accumulated over four years of garage testing, DSP paper reading and random internet detours. That pile became the seed corpus. After three months of daily use, roughly 94% of my retrieval attempts succeed in under 10 s, by keyword or by meaning — a number that comes from an actual log, since the app records locally whether each search session ended in an open or an abandon. The remaining 6% are honest failures, mostly items whose extracted text came back empty: screenshots of screenshots, largely. atuan uses Stashio for the API documentation snippets he needs during backend work. ktuyen attaches test-case screenshots and regression logs to Spaces before each release cycle, which means the QA history of every uranashel release now lives inside one of the apps being QA'd. Nobody planned that. ## Same studio fingerprints Stashio is not a physics instrument, and no Kalman filter hides in its settings screen. It still looks like us. The same monochrome UI language, the same [Nunito on every label](https://uranashel.com/blog/nunito-on-every-pixel.html), the same 12 px corner radius, the same bilingual EN/VI strings that ktuyen reviews line by line. The design system is documented in [the monochrome post](https://uranashel.com/blog/monochrome-ui.html), and it transferred to a bookmarking app without modification — we take that as evidence it was a system rather than a coincidence. The app page is [here](https://uranashel.com/apps/stashio.html). Stashio solves a different class of problem from Wheria, and that difference is exactly why it lives in the portfolio alongside Wheria instead of replacing it: the sensor apps prove the phone can be an instrument, and Stashio proves the same three people can respect a completely different set of constraints when the problem asks for them. --- uranashel · [Home](https://uranashel.com/) · [Apps](https://uranashel.com/apps.html) · [Lab](https://uranashel.com/lab.html) · [About](https://uranashel.com/about.html) · [Blog](https://uranashel.com/blog/) · [Developers](https://uranashel.com/developers/) · [API docs](https://uranashel.com/docs/) · [Privacy](https://uranashel.com/privacy.html) Machine-readable: [llms.txt](https://uranashel.com/llms.txt) · [sitemap.xml](https://uranashel.com/sitemap.xml) · [openapi.json](https://uranashel.com/openapi.json) · [API](https://uranashel.com/api/v1/)