# Copyright (c) 2026 Krzysztof Rudnicki """Which apps keep a desktop Firebase session, and why the list is hand-kept. Split out of ``seed_session.py`` for the 250-line cap. The rationale is worth more than the tuple: it is what stops the next person from "simplifying" this into a directory scan. """ from __future__ import annotations from typing import Final # Every app that calls firebase_client_for/mirror_client_for from the desktop, # and so keeps a refresh token under ~/.config//. Named here rather than # discovered from ~/.config: a missing directory is exactly the case that # needs seeding, so discovery would skip the app that needs this most. # The remaining Flutter-only apps (home_inventory, workout_app) are absent on # purpose: they authenticate in-app on the phone and have no desktop session. # `todo` used to be grouped with them, but it grew a real desktop wrapper # (lib/desktop/wrapper_server.dart) with its own Firebase REST client that # needs a session exactly like the Python daemons do -- see # lib/sync/firebase_backend.dart and lib/desktop/wrapper_server.dart in # ~/todo. Its credential cache lives at ~/.config/todo/firebase_auth.json, # same shape as every entry below, even though the reader is Dart, not # Python: FirebaseCredentials.fromJson in crdt_sync_dart parses the exact # {id_token, refresh_token, expires_at} shape credential_store_for writes. # # Re-derive rather than guess when adding one; a repo-by-repo sweep missed two # of these. Grep all of ~ for the string literal passed to firebase_client_for, # mirror_client_for and credential_store_for across every *.py. That still # misses call sites passing a constant (wake_alarm._constants.SYNC_APP_NAME, # home_guard._constants.APP_NAME), so check those by name too. "interop" is # tool/interop_seed.py's own scratch store, not an app. DEFAULT_APPS: Final = ( "diet_guard", "wake_alarm", "screen_locker", "byox_ladder", "leetcode_guard", "todo", "home_guard", # The shared free-day pool (~/src/utils/freedays), not an app: its systemd # timer is headless and it has no session of its own to inherit, so # without this a day marked on the phone never reaches the PC. "freedays", # The shared sick-day pool (~/src/utils/sickdays): headless like freedays, # and the screen-locker "I'm sick" button pushes through it too. "sickdays", # The daily RTDB backup (~/src/dufs-cloud/firebase_backup). A headless # user timer with its own session, so it never races an app's token file. "firebase_backup", # ~/src/daily-limits: its user timer publishes today's limits for the # phone app and answers its requests, with a session of its own. "daily_limits", # ~/src/tutor: its PC worker answers the Flutter client's requests over # RTDB (/tutor/v1), headless, with a session of its own. "tutor", # ~/src/sufler: its PC TTS relay (tts/sufler_tts/relay/setup.py) answers # the Flutter client over RTDB (/sufler), headless, reading # ~/.config/sufler/firebase_auth.json via credential_store_for("sufler"). "sufler", # ~/src/kartka's headless agent instance (tools/agent): its wrapper serves # this file once per launch as /sync-credentials, so the web build signs # in with no password and no browser consent of its own. "kartka-agent", # ~/src/kartka/tools/source_eval: the weekly source evaluator publishes # /kartka/source_health via credential_store_for("kartka-source-eval") # and falls back to the retired password only with no session here. "kartka-source-eval", # ~/src/todo/mcp: the Dart todo MCP server (systemd --user, :8735) is one # more sync node with its own session, read once from # ~/.config/todo-mcp/firebase_auth.json at start. "todo-mcp", )