All projects
03MOBILE APP2026
Pushlog app icon: an angular lime mark on a dark background

Pushlog

A push-up training app that turns a longer-term goal into one clear action today — camera-counted reps, an honest ledger, and friends one rep behind.

View on GitHubiOS · Android
Pushlog sign-in screen: "Turn a push-up goal into a plan you can keep" with a Continue with Google button
Pushlog Today screen: 26 reps, a recovery-day note, Start a counted session, and a Friends card saying you are leading today
Pushlog week view: 176 reps this week as a bar chart, 500 all-time, best day 75, 5-day streak, with +1 +10 +25 quick-log buttons

Overview

What is Pushlog

Pushlog is a push-up training app for iOS and Android that turns a long-term goal into one clear action today: an adjustable plan, fast daily logging, camera-counted sessions, and friends who can see exactly one rep ahead of you. It is Ngonidzashe Mangudya's open-source project (MIT), built in Flutter on a Cloudflare Workers + D1 API, with Firebase for identity and push.

Role
Open-source contributor — the product and its architecture are the creator's work; I send changes upstream.
Created by
Ngonidzashe Mangudya
Platform
iOS · Android
Stack
Flutter · Cloudflare Workers · D1 · Firebase Auth
Native
HealthKit · ARKit · Watch (iOS)
Status
v1.1.0 · MIT · dev / staging / prod flavors
Source
iamngoni/pushlog

The project

What it does

  1. 01A plan-first loop: onboarding turns a long-term push-up goal into an adjustable 8-, 12- or 16-week plan with planned rest days, a recovery day is honest that there is nothing to make up, and logging is one tap — +1, +10, +25 — or a counted session.
  2. 02Counted sessions run on an ARKit/TrueDepth bridge that counts reps from the camera and auto-pauses when the face anchor goes stale; manual corrections are recorded separately from tracked reps, and finished sessions land in HealthKit on iOS.
  3. 03Application data lives behind a Cloudflare Worker on D1 — profiles, usernames, workout snapshots, daily scores, friend graphs and leaderboards — with Firebase kept only for identity and push; Firestore remains as the historical migration source, reconciled against SHA-256 checksums.
  4. 04The API owns identity: every /v1 route is scoped to the uid inside a Firebase ID token verified against Google's JWKS — a uid in a request body is never trusted.
  5. 05The friends leaderboard survives flaky push: being overtaken is derived inside the workout write, fanned out with waitUntil and always written to a D1 inbox the app polls — FCM is an optional transport.
  6. 06Android is honest about the gap: iOS-only camera, HealthKit and watch capabilities report as unavailable rather than faking parity, and manual logging stays first-class on both platforms.

Screens

Pushlog Today screen showing 128 reps, 12 minutes, a 7-day streak, Start session and Log reps buttons, and a friends board
Today leads with one action. Everything else — streak, time, the friends board — sits under the two ways to record a set.
Live counted session screen with a timer, a rep count of 18, a depth indicator, pause and end controls, and Auto/Manual toggle
A counted session: live rep count, the current phase, tracking status, and +1 / +5 / +10 corrections that are stored as manual adjustments.
Session saved summary listing 18 reps added, Apple Health saved, cloud synced, friends board updated and counting confidence
The save summary is deliberately literal about where the session went — Health, cloud sync, the friends board — and how confident the count was.
Pushlog History screen with monthly totals, best day, average, a weekday bar chart and a recent sessions list
History distinguishes counted sessions from manual logs, and shows a rest day as a dash rather than a failure.
Friends screen with activity visibility control, username search, pending requests and a ranked friends list
Friends: username search, incoming and outgoing requests, per-scope visibility, and a leaderboard the Worker ranks by today's reps.
Health and Watch settings listing Apple Health authorization, workout type, Apple Watch availability, workout owner and counting source
Platform capabilities are surfaced as state, not assumed: authorization, watch availability, workout ownership and counting source are all visible and reversible.

How it works

How the data moves

The core paths through the system — a dot travels each hop, and the caption says what happens there.

A counted session

From tapping start to a synced day — the camera counts, the API only writes what changed.

YESNOStart
Start a counted session
Camera preflight locks onto your face
Count reps · auto-pause if the face anchor goes stale
Finish — save the session to HealthKit on iOS
PUT /v1/workout-snapshot (day totals + sessions)
Snapshot hash changed?
Write sessions + daily scores to D1
202 with syncId and content hash
End
200 — nothing to write

Swipe sideways to see the whole chart.

01 / 11tap

You start a counted session from Today.

Decisions

Calls that shaped it

Workers + D1 instead of Firestore

Relational queries for leaderboards and day totals, one schema, a cheap edge runtime — Firestore stays only as the historical migration source.

Sync the whole day snapshot, hashed

The app PUTs its day totals and sessions as one snapshot; the API compares a SHA-256 and only rewrites on change — idempotent, and friendly to patchy connections.

Honest capabilities on Android

iOS-only camera, HealthKit and watch features report as unavailable rather than faking parity; manual logging stays first-class everywhere.

Flutter as the primary client

One codebase replaced the Swift app, which is kept as the reference implementation until every native bridge is verified on real devices.

Stack

  • Flutter
  • Dart
  • Cloudflare Workers
  • D1
  • Firebase Auth
  • FCM
  • HealthKit
  • ARKit