Rejog Stash: Your Stuff, Findable and Lendable

6 min read Updated August 13, 2026

Published by Dan Richardson at https://freepdx.com/deep-dives/rejog-stash

Team
Solo
My contribution
Owner and principal engineer
Outcome
The iPhone app, borrower web experience, and backend are live. The Android app is built and working, and is still waiting for Play Store approval.
Status
Production: iPhone and web live; Android awaiting Play Store approval
Constraints
  • Native iOS, native Android, and web each have their own codebase and use the same versioned API.
  • Borrower privacy is enforced through separate storage and server-side trust boundaries.
  • Android store release remains outside the shipped iPhone, web, and backend path.

Rejog Stash went from a napkin sketch to live on the App Store in 6 weeks. The complete codebase includes 2 mobile apps (Swift on iOS, Kotlin on Android), a web app, and a Cloudflare backend - including a complete moderation module that was not in the original plan! I built it solo while working on three other projects.

In building Stash, I had two goals:

  • Use Latticeflow to create an application - agentic from start to finish - that solves a real problem
  • Push the process hard enough to find where it breaks, so I could learn and improve it

I also wanted code I could show instead of describe. Most of what I have built is owned by someone else and sits behind an NDA. I built Stash with Latticeflow, my agentic development process, and throughline-build (Build) is my implementation of it. I created it so I could learn why they make the choices they do, and how to guide them. It’s written in C#/.NET, cross platform, and free as in air… or beer (I always get those 2 confused).

Rejog Stash was born

I’m obsessed with memory - not the silicon kind - but real, human, fallible, ephemeral memory. I have a hard time keeping track of things, and never remember who I lent it to. So why not write an app to catalog my stuff and help me remember?

With peer-to-peer lending. AI to identify things in photos. GPS to remember where it is. QR codes instead of accounts. Native iOS and Android and infrastructure. Latticeflow as the build process.

Tech Stack

Languages
  • Swift
  • Kotlin
  • TypeScript
  • JavaScript
  • HTML/CSS
  • SQL
  • Python
Runtime & frameworks
  • iOS SwiftUI
  • Android Jetpack Compose/AndroidX
  • Cloudflare Workers
  • static browser frontend
  • CameraX
Data & storage
  • iOS SwiftData + CloudKit/private DB + Keychain/iCloud Keychain
  • Android Room/SQLite + DataStore/EncryptedSharedPreferences
  • Cloudflare D1/KV/R2
Infra & deploy
  • XcodeGen/TestFlight/App Store Connect scripts
  • Gradle/R8/Firebase App Distribution/Play tracks
  • Wrangler/Workers Assets/custom domains
  • D1 migrations
AI / ML
  • iOS Vision/Core ML MobileCLIP/FoundationModels/OpenRouter relay
  • Android ML Kit/ONNX Runtime Mobile/MobileCLIP model download
  • Worker api-router relay to OpenRouter
Testing
  • XCTest/XCUITest
  • Android JUnit/Robolectric/Compose UI tests/MockWebServer
  • Vitest/node:sqlite
  • Playwright
  • Python device integration harnesses
Notable libraries
  • StoreKit 2
  • Play Billing
  • APNs/FCM/Web Push
  • Resend
  • osmdroid
  • Retrofit/OkHttp/kotlinx.serialization
  • Coil
  • ZXing

How It Works

Tap the camera button and take a photo. Vision detects objects, sends them through an owner-authenticated relay to be identified. iOS 27 will bring on-device AI so identification stays on the phone - and it’s pretty good! There’s a local fallback if you want to stay offline, too. That’s how the catalog is built - you shouldn’t have to enter text or ids unless you want to.

To search, you type in a word that becomes a needle query into curated haystack that includes OCR text pulled off the photo. A book’s ISBN is searchable and you didn’t have type it in.

Want to lend somebody a drill? Show them a QR code or send them a link. They don’t need an account - that link you sent them is like a one-time key to a storage locker.

Rejog Stash: one shared API, three independent clients Three clients sit at the top: the iOS app, persisting to SwiftData mirrored to a private CloudKit database, where item custody and condition are derived by folding an event ledger; the Android app, split into two physically separate Room databases, a syncable CatalogueDatabase for owner inventory and a device-local BorrowedDatabase that structurally never reaches a sync path; and the borrower web app, which holds no local store at all and authenticates purely by possessing an unguessable capability link. The iOS app and the Android catalogue database push HMAC-signed writes down to a single Cloudflare Worker at stash.rejog.net, which exposes a version 2 API that changes only through backward-compatible additions. API changes are tracked and reviewed in the web repo. The Android borrowed database has no connection down to the Worker at all, marked as no sync path. The Worker serves the borrower web app a capability link with no account required. Below the Worker sit its three storage bindings: D1, a relational system of record with 26 migrations across about 27 tables; KV, for hot token lookups, rate limits, idempotency, and WebAuthn challenges; and R2, two buckets, one for item photos deduped by content hash and one for the roughly 87 megabyte on-device MobileCLIP encoder Android downloads on first run. Rejog Stash one shared API, three independent clients iOS App SwiftUI + SwiftData mirrored to private CloudKit custody/condition: folds over an event ledger, re-run on merge feature design starts here Android App CatalogueDatabase owner inventory - syncable BorrowedDatabase device-local - never syncs Borrower Web no app, no account identity = holding an unguessable link no local store at all Cloudflare Worker - stash.rejog.net versioned API (v2) - additive & backward-compatible API changes are tracked and reviewed in the web repo HMAC-signed writes catalogue only, API v2 no sync path capability link, no account D1 (SQLite) relational system of record 26 migrations, ~27 tables KV tokens, rate limits, idempotency short-lived WebAuthn / pairing challenges R2 - two buckets PHOTOS - content-hash deduped MODELS - ~87MB encoder, first-run download
Three independent clients, one shared API. iOS and Android each own their local state; Android's borrowed items are structurally cut off from the sync path; the Cloudflare Worker exposes the versioned API all three clients use.

Rejog Stash architecture. Privacy is enforced in the code, not in a policy. Location is only needed at the moment of capture - never in the background. Contact details are only sent to remember the loan.

Two Planes, Built While Flying

The prudent approach would have been to build one platform, stabilize it, then port. I could have taken the time and done that. But I wanted to see where the process broke, and building two native clients at once is a good way to find out. I did it deliberately.

iOS and Android were built in parallel. iOS was the lead platform for design and features. Android ported a feature from iOS as soon as it was committed, but implemented with native Android patterns. Two planes built while flying, one taking cues from the other mid-air.

I also decided I needed a web app, so… three planes. 😅

The Wire Contract

That only works if the thing holding them together is rigid. The codebases don’t share a model layer, but I gave the agents structure - an API, a strict rule on how to change it, and a requirement to be good communicators. They called it The Wire Contract. Sure.

Infrastructure owns the API. It’s additive only, so changes don’t break shipped apps. The app agents file change request tickets, and implement their side on approval.

build also installs a cross-impact skill. It requires agents to ponder one question: did what you just implement materially affect another project? If so, file a ticket there so it can react. Each repo’s .build/conductor.toml names its siblings and the file that owns the contract, so nobody is guessing. If the check cannot run, the instruction is to stop, not improvise.

This isn’t new. It’s just standard processes that most Agile teams use every day. I found out how much more it mattered in Agentic development. With a single human-on-the-loop - human context management is critical. The only thing that keeps the agents from drifting when you’re moving this fast is strict boundaries. Otherwise things go off the rails.

What Made It Hard

Some technical things were tough - fast, on-device object identification for one. But building three clients, in parallel, by myself, without breaking things or drifting? That was hard.

Validation and QA was a challenge, but it was as automated as I could make it. Agents use a harness to debug on-device. I have 1,300 test cases, and reviewer agents require screenshots to verify AC. And I built a physical rig four phones (iPhones and Android), through the live lend-borrow cycle.

Where It Bent

I wanted to know how far this could be pushed, and oh-boy I found out. 6 weeks of continuous human work - bye-bye Summer, hello Fall! - was the cost. It was real calendar time, and even with agents, it was still an enormous amount of work. Agents don’t sleep, and I didn’t get much either.

That came at a cost, but this was an experiment. I allowed myself to make decisions I normally wouldn’t. I was the stakeholder, reviewer, and the only user who mattered. I decided when to ship. If this were any other project, there would be a change-approval path, sprint reviews, real QA, and an independent security review I didn’t write myself. More human hands, eyes, and brains.

The education was very valuable, and I’ll never do it again.

Scope. Stash crept, badly, and I let it. Claude and I added some cool features. But that moderation module? It was never in the plan. Google required it because sometimes a photo labeled Sawzall is not what it appears to be. Humans, the Internet, and Why We Can’t Have Nice Things. Some creep was me learning how to manage the Agents. Figuring out the right mix of words and controls is what Agentic development is all about

But it did ship. The process worked. 1173 tickets moved across the board in 6 weeks. And the code is good.

The Throughline

I want to create software that makes the world better. Software is personal, and everything I build starts with someone I want to help. Stash is one slice of Rejog, shipped early and built primarily for me - to learn if the Latticeflow process would work in the real world. It did.

The hard parts - managing the API across three clients, vision running on the phone, privacy enforced in code instead of in a promise - are all reusable. Rejog is working it’s way through development, more on that after I check out this thing called The Sun. 🌞

The throughline, however, is done.

Status

The iPhone app is live on the App Store. The borrower web app and backend are live at stash.rejog.net. The Android app is built and working, and in the Play Store queue.

Rejog is the product brand. Rejog Stash is built by Throughline Tech, LLC and distributed on the App Store by FreePDX.

Pinch or drag to inspect