Our work

What we can build, and what we have built

Each with the work behind it and the stack it was built on.

Mobile apps

Native iOS and Flutter, held to a security bar most internal tools never reach.

A customer's name cannot end up in a log file. Not should not. Cannot.

A Lock Screen Live Activity showing a match score updating in place, with the app name, the live minute and the venue
Three apps on the App Store. The onboarding app runs on bundled fixtures.

A new-hire onboarding app on Clean Architecture, with module boundaries enforced by SPM rather than by convention: Domain compiles against Foundation alone and the tests run against it in isolation. Entra ID sign-in through the Microsoft broker. An AI concierge on Apple's on-device Foundation Models, so nothing leaves the phone. Secure Enclave biometric ACLs on the keychain, SPKI certificate pinning with a backup pin, and a sealed log taxonomy that makes writing an unmasked name or ID into a log impossible rather than discouraged. OWASP MASVS L2 and Swift 6 strict concurrency throughout. Separately, a tournament companion driving Lock Screen Live Activities and all three Dynamic Island states over APNs, with 104 fixtures, 16 host-city guides, and no analytics SDK of any kind.

  • Swift 6
  • SwiftUI
  • ActivityKit
  • WidgetKit
  • MSAL / Entra ID
  • Foundation Models
  • Flutter

Checkout and payments

Card payments through Square's hosted fields. Card details never reach the site.

Nobody can change what they are charged by editing the page. The price is decided on our side, every time.

The payment step of a checkout: accepted card brands, Square's hosted card field, and a line saying card details go straight to Square and never touch the site
Storefront live. Payments verified end to end on Square sandbox; production credentials sit with the client.

A storefront with a cart, four pickup locations and Square Web Payments. The amount is computed on the server from our own price list, so a page that posts its own total cannot be made to post any total. Repeat submissions collapse to one charge through an idempotency key held across retries, proven by submitting the same order twice and getting one payment id and one charge. Tested against every decline Square offers, wrong CVV, wrong postal code, expired date and declined card, each returning wording written for a buyer rather than the processor's error code. We also found the storefront adding 8.25% sales tax that Texas does not levy on bakery items, and removed it.

  • Next.js
  • Square Web Payments
  • TypeScript
  • Vercel

Work that runs overnight

Systems that collect, send and report on a timer, and stop when they should.

We measured that only 13 to 19% of the client's market was reachable at all, and said so before they paid for it.

Delivered. Findings published.

A platform that finds businesses in a US ZIP code, crawls their sites for a published address, and sends from the client's own mailbox over OAuth. Roughly 9,600 lines across 65 modules and 39 numbered SQL migrations, with 49 tests and 18 database assertions. One-click unsubscribe to RFC 8058, and a suppression trigger firing on unsubscribe, complaint or hard bounce that is checked again immediately before each send rather than only at queue time. The research came with it: 2,843 Austin businesses sampled, a real mailbox ceiling of about 40 cold emails a day against a published limit of 2,000, and a written warning that the industry's standard workaround is the pattern Google's own policy names as prohibited.

  • Next.js
  • Postgres
  • Microsoft and Google OAuth
  • Vercel Cron

Reporting and data

Numbers pulled from the source, on a schedule, into one place.

Both pipelines run on free tiers, so watching the numbers costs nothing to operate.

Running on a schedule.

Sales and analytics pulled straight from App Store Connect against an ES256-signed key, with no third-party dependencies beyond the crypto library. A Cloudflare Worker checking App Store keyword rankings weekly and pushing week-over-week movement to Telegram.

  • Python
  • App Store Connect API
  • Cloudflare Workers
  • Telegram

Location and camera apps

Products where the phone's own sensors decide what a user is allowed to do.

We sign people in and then throw the name and the email away. If that database ever leaks, there is nothing in it worth taking.

Runs on device against local data. The Worker and its migrations are written but not yet deployed, so it is not end to end.

A campus question board where posting and voting sit behind a 0.6 mile geofence, so being there is the credential and there is no email domain to check. Outside the fence the app is readable but inert. Identity is deliberately thin: the sign-in returns a name and an address, both are discarded, and only the opaque user identifier is kept, which is enough to stop bot posting and to carry a score across reinstalls while being useless to anyone who takes the database. Answers decay on a timer rather than living forever, so the feed reflects what is true this week. There is an AR mode running ARKit world tracking with a Vision classify loop on a 1.5 second cadence. 2,233 lines of Swift on SwiftData, with a 604-line Cloudflare Worker behind it, eight endpoints, a half-hour cron for decay, and 220 lines of SQL migrations.

  • SwiftUI
  • SwiftData
  • ARKit
  • Vision
  • Core Location
  • Sign in with Apple
  • Cloudflare Workers
  • D1

Wearables and sensor data

Software on a watch, where there is no server to fall back on and every sensor is optional.

The drive starts when the phone connects to the car stereo. The driver is not asked to do anything.

Runs on device, built for fenix5plus, vivoactive4 and venu3. Not published to the Connect IQ Store.

A Garmin watch app that scores how close a driver is to falling asleep, with an Android companion that opens and closes the session on its own. The companion listens for a Bluetooth connection and filters it against one saved car name, so pairing with headphones does not start a drive, then queues a message and delivers it once the SDK, the watch and the app are all ready rather than assuming they are. Disconnecting ends the session. On the watch the score fuses eight signals: heart rate, heart rate against its own recent average, Body Battery, time since the last movement, blood oxygen, stress, respiration rate and time of day. Every one of them is optional in the code, so a model missing a sensor scores on what it has instead of failing, and signals subtract as well as add, because high stress and fast breathing are evidence of alertness rather than the absence of drowsiness. The app also asks the wearer what actually happened and stores up to 200 timestamped labels per stream, which is the only way to learn whether the score was ever right. Logging is a 200-entry ring buffer with 50 kept across restarts, written as arrays rather than strings to stay inside what the watch can store.

  • Monkey C
  • Connect IQ
  • Kotlin
  • Jetpack Compose
  • Connect IQ companion SDK

Staying on the right side of US rules

The regulations that decide whether a system is an asset or a liability.

One bad email can cost $53,088, and the FTC says hiring someone else does not move that off you. So the rule goes into the system, not into a disclaimer nobody reads.

In production on every project it applies to.

On the outreach platform, a campaign cannot be created without a physical postal address, because the column is not null rather than because a checklist says so. Opt-outs are honored immediately by a database trigger on unsubscribe, complaint or hard bounce, and the suppression list is checked again in the moment before each send rather than only when the job was queued. One-click unsubscribe to RFC 8058. Elsewhere: an iOS app for US medical billing labeled informational rather than advisory, since that word is the line between help and liability; a storefront we found charging 8.25% sales tax Texas does not levy on bakery items; and apps that ship with privacy manifests declaring no tracking, no ad identifiers and no third-party data sharing, because a US buyer increasingly gets asked about that by their own customers.

  • Postgres
  • RFC 8058
  • CAN-SPAM
  • Apple Privacy Manifests

If what you need is not listed, tell us the problem and we will say whether we can do it.