Skip to main content

TidyShelves – Architecture, Sync & Subscriptions

📚 TidyShelves · Reference Blueprint

Architecture, Sync, Subscriptions & Onboarding

This page is the single source of truth for TidyShelves server and iOS implementation phases: sync, subscriptions, device limits, onboarding invites, and offline-first UX.

🧩 It reflects what’s already implemented (Phase 0 & Phase 1) and what’s planned next in a server-first sequence.

Current Status:

✅ Phase 0 (iOS local-only) · ✅ Phase 1 (Entity Sync Backend) · ⏳ Subscription, Tenant/User, Invites, Limits & iOS sync pending

🔄 Sync Engine💳 Subscriptions🪪 Tenants & Users📨 Invitations📱 Multi-Device📲 Offline-First iOS

🧠 Core Concepts

TidyShelves is tenant-centric. Identities from Cognito/Entra/Okta are front doors into tenants, but the source of truth for users, subscriptions, devices and invitations lives in our DB.

🏠 Tenants & Users

  • Each tenant corresponds to a household or small business.
  • First user → creates a new tenant and becomes the Owner.
  • Additional users join via invitation deep links into the same tenant.
  • user_identities map IdP identity → internal user_id.

🔄 Sync & Change Log

  • Single endpoint: POST /data/entities/sync handles push + pull.
  • Server applies mutations, records changes in entity_change_log with version.
  • Clients track lastPulledVersion per device.
  • PostgreSQL functions implement upsert, move, soft-delete, restore, purge.

💳 Subscriptions & Limits

  • Plans define limits: items, users, devices, photos per item, and sync cadence.
  • Sync cadence is expressed as syncIntervalMinutes in backend config (e.g., Manual / 6 hrs / 3 hrs / 1 hr / 10 min / Real-time).
  • Config service exposes effective subscription config to data/file services and iOS.
  • iOS uses cadence as a hint for when to auto-attempt sync (offline-first preserved).

📨 Invitations & Onboarding

  • Owners send invites via user_invites table + email deep links.
  • Invited user taps deep link → IdP login → backend GetOrCreateUserAsync.
  • Invite ties new user to the existing tenant (subject to UsersLimit).
  • Flow is similar to Netflix / household invites, but tenant-scoped.

🗺 Server-First Implementation Roadmap

The roadmap is intentionally server-first: complete all core backend building blocks and exercise them via Postman before wiring the iOS app into sync, subscriptions, and invites.

Phase 0
iOS Local-Only Baseline
  • Core Data + UI stable with zero network dependency.
  • EntitiesStore supports create/update/move/soft-delete/restore.
  • Recently Deleted, move picker, detail view, and trash semantics in place.

Already Implemented

Phase 1
Entity Sync Backend (Server)
  • POST /data/entities/sync implemented.
  • Idempotent mutations via entity_mutation_applied.
  • PostgreSQL: entity_upsert, soft_delete_entity, restore_deleted_group, purge_deleted_group, move_entity_tree.
  • Changefeed via entity_change_log with monotonic version.
  • Pending micro-fix: treat “delete non-existent entity” as true no-op and skip change log.

Already Implemented (with minor fix remaining)

Phase 2
Entity Sync Testing (Postman)
  • Postman collection for all mutation types (Upsert, Move, SoftDelete, Restore, Purge).
  • Scenarios: create→delete→restore, move subtrees, pagination with HasMore, idempotent replays.
  • Validate delete-before-create classical bug is handled gracefully.

Already Implemented

🛠
Phase 3
Subscription Service (Server)
  • Create subscription tables: subscription_plan, tenant_subscription.
  • Define limits: maxItems, maxUsers, maxDevices, maxPhotosPerItem.
  • Add cadence: syncIntervalMinutes to subscription_plan (null or 0 = manual-only).
  • Add trial metadata to tenant_subscription: trialDays, trialEndsAt, status (trialing, active, canceled).
  • Implement GET /config/subscription returning effective config per tenant, including syncIntervalMinutes and current trial state.
  • Seed initial plans (Starter, Solo, Essentials, Pro, Business, Enterprise).

Pending

🧪
Phase 4
Subscription Testing (Postman)
  • Postman: verify plan configurations for multiple tenants.
  • Simulate upgrades/downgrades and check resulting config.
  • Test behavior when tenant has no explicit subscription row (fallback to default plan).

Pending

🛠
Phase 5
Tenant & User Registry (Server)
  • Implement tenants, users, user_identities tables.
  • Create GetOrCreateUserAsync(ClaimsPrincipal, invitationId?).
  • On first login (no invite), auto-create tenant + owner user on the Starter (free) plan.
  • When a paid plan (Solo, Essentials, Pro, Business) is selected for the first time, create a tenant_subscription row with status = "trialing" and trialEndsAt = now + trialDays.
  • After trial expiry, automatically transition to status = "active" (or enforce downgrade) based on billing outcome.

Pending

🧪
Phase 6
Tenant/User Testing (Postman)
  • Postman tests: first login → tenant + owner bootstrap.
  • Ensure user_identity correctly maps provider subject to internal IDs.
  • Validate behavior on repeated logins and crossing tenants.

Pending

🛠
Phase 7
Invitation & Onboarding Service (Server)
  • Implement user_invites table with expiry, status, and invited email.
  • POST /auth/invite (owner-only) → returns deep link (e.g., tidyshelves://invite?token=...).
  • POST /auth/bootstrap accepts optional X-Invite-Id or invite token and attaches the user to the correct tenant.
  • Enforce maxUsers from subscription before accepting invite (trialing or active).
  • Ensure that joining during a trial keeps the tenant on the same trialEndsAt date (no per-user trial resets).

Pending (critical multi-user onboarding piece)

🧪
Phase 8
Invitation Testing (Postman)
  • Generate invites as an owner via Postman.
  • Simulate “invite acceptance” by calling bootstrap endpoints with tokens.
  • Test expired/used/invalid invite error paths.
  • Test hitting UsersLimit and correct error responses.

Pending

🛠
Phase 9
Device Registry & Limits (Server)
  • Implement user_devices table.
  • POST /device/register to associate user_id + deviceId.
  • Validate against subscription config maxDevices.
  • Prepare response contract for iOS (allowed / over-limit).

Pending

🧪
Phase 10
Device Limits Testing (Postman)
  • Register multiple devices via Postman and verify limit behavior.
  • Confirm correct responses for over-limit scenarios.

Pending

📲
Phase 11
iOS Outbox & Local Sync Plumbing
  • Introduce CD_SyncOutbox + CD_SyncState into Core Data.
  • Wrap all local mutations with outbox enqueueing.
  • Respect syncIntervalMinutes when scheduling background/foreground auto-sync attempts.
  • Ensure background-safe Core Data writes and consistent IDs.

Pending (first iOS sync phase)

🔁
Phase 12
iOS SyncCoordinator
  • Push outbox → /data/entities/sync, handle mutation results.
  • Pull changefeed → apply via background context, update lastPulledVersion.
  • Trigger sync if lastSuccessfulSyncAt exceeds cadence threshold (based on plan).
  • Triggers: manual, app foreground, network-back-online, debounced local changes.

Pending

💳
Phase 13
iOS Subscription-Aware UX & Limits
  • Fetch /config/subscription on app launch / refresh.
  • Enforce local item limits even when offline (friendly banners).
  • Surface device over-limit and UsersLimit errors in understandable UI.

Pending

📈
Phase 14
Telemetry, Polish & Final QA
  • Instrument sync timings, failure categories, and retries.
  • Measure subscription limit hits and invite flows for product insights.
  • Polish UX flows and documentation.

Pending