Skip to content

All workMindy

One person's data on another's screen.

A Brazilian app for a shared home: what belongs to the home is visible to everyone, what is personal stays private. I am the co-founder and the only developer; my partner handles brand and sales. I built it between July and October 2026 and kept it in private beta by invitation until the pause. It is paused by my decision while I focus on other projects. The code is private, and what I show here are the parts I can demonstrate without exposing anything.

Status
private beta, paused
Period
Jul to Oct 2026
Stack
Flutter and Dart, Supabase (Auth, Postgres, Edge Functions, pg_cron, pg_net), Web Push and FCM, pgTAP, GitHub Actions

Done by me

Product, app, database, edge functions, tests and CI.

Context

My own product. Brand and sales with my partner.

Inside

The rule lives in the database

A private record has an owner; a shared record has a home. Reading the home goes through a function that checks whether you are an active member right now. Leaving the home cuts access at once. The tests take on the identity of two people in sequence and plant personal "canaries" to check they stay private after those people join the same home.

SQL
create function public.is_own_active_house_member(p_house_id uuid)
returns boolean language sql stable security definer set search_path = '' as $$
  select exists (
    select 1 from public.house_members as member
    where member.house_id = p_house_id
      and member.user_id = auth.uid()
      and member.left_at is null);
$$;

create policy house_tasks_select_active_member on public.house_tasks
  for select to authenticated
  using (public.is_own_active_house_member(house_id));

Web Push from the RFCs

The standard library depends on Node APIs the edge function does not have. The encryption (RFC 8291 and 8188) and the VAPID token (RFC 8292) use only WebCrypto, in 368 lines, and I checked them against the official RFC example, byte by byte. A later audit found the missing risk: the push address lives in a table the user writes to, so a forged subscription made the function call any host. Today only allowed hosts go through.

TypeScript
// RFC 8291 §3.3-3.4: IKM from the ECDH secret and the browser auth
const keyInfo = concat(encoder.encode('WebPush: info\0'), uaPublicRaw, localPublicRaw);
const ikm = await hkdf(authSecret, sharedSecret, keyInfo, 32);

// RFC 8188 §2.2: GCM key and nonce
const contentKey = await hkdf(salt, ikm, encoder.encode('Content-Encoding: aes128gcm\0'), 16);
const nonce      = await hkdf(salt, ikm, encoder.encode('Content-Encoding: nonce\0'), 12);

// One record only, and the last: delimiter 0x02
const record = concat(plaintext, new Uint8Array([0x02]));

The queue is the guarantee, the call is the hurry

Creating a task writes the notice to a queue in the database. The immediate call is only hurry and can fail freely: the exception is swallowed on purpose. A cron every 2 minutes redelivers what stopped, up to 6 attempts. And the receipt separates accepted, shown and clicked, because "the service accepted it" is not "the device showed it".

The AI has no pen

The model interprets free text and returns a structure. What writes is always a versioned function in the database, with a stable request ID: a double tap creates one task. Destructive or ambiguous actions ask for confirmation; simple voice creations come with a receipt and Undo. With no network or no quota, it falls back to a local interpreter at zero cost.

How I know it works

The last CI run, on October 12, 2026, passed 3,159 Dart tests (7 skipped), 1,782 pgTAP assertions in 75 files and 82 edge function tests, plus a secret scan, static SQL guards and acceptance on an Android emulator at two API levels.

What this does not do

  • It is paused: the last commit is from October 12, 2026.
  • It is not open to the public: it is a private beta by invitation.
  • The static guard does not see whether a policy isolates the right rows, and it says so in its own output. pgTAP checks that.

Screens

Today screen in Mindy, with the day agenda and sample data
Today, with sample data.
Shared shopping list in Mindy, with sample data
The list the whole home sees.

What broke

  1. 42501 permission denied for schema private.

    A deferred trigger runs outside the function with permission; tests end in rollback and never fired it. Now it runs as owner.

  2. HTTP 2xx, and nothing on the iPhone screen.

    Orphan subscriptions. A receipt in three layers, and a cleanup.

  3. otp_expired.

    The provider robot used up the link. Now the token is traded by POST, on my page.

Credits: My own product, with my partner on brand and sales. Private code.

Tell me the problem.

One message is enough to start. I reply myself and, if it makes sense, I send a written proposal: what is in, what is out, deadline and price.

WhatsApp +55 11 99227-9903