diet-guard/diet_guard/_sync_paths.py
Krzysztof kuhy Rudnicki fd0a35e69e feat: sync the catering credential as its own document
The phone is getting its own Kuchnia importer, and it cannot fetch anything
until the panel password reaches it. So the credential syncs, and a phone that
has been wiped and reinstalled gets it back without the user digging out the
original.

**It travels in plaintext.** Nothing here encrypts it and the rest of the
synced state is not encrypted either, so the docs no longer say a credential
"must never leave the machine it was entered on" -- that paragraph is rewritten
rather than given an exception clause. What still never leaves is the session
cookie: it is regenerable from the password, so syncing it would widen exposure
and buy nothing.

Its own document, not a field on `budget`. `budget.json` is written back at
default permissions, so a password riding inside it would be readable by
anything that reads the budget. Two independently-clocked fields, so a device
that only ever set the username cannot blank a peer's password.

The hand-written `kuchnia_credentials` keeps its contract -- written by the
user, never by this package -- and now acts as a local override that wins over
the synced copy, plus the bootstrap for the very first push. Without that
fallback no device would ever publish, since the synced cache starts empty on
every device.

Two bugs the tests caught rather than review:

- `_local_credential_log` imported `KUCHNIA_CREDENTIALS_FILE` straight from
  `_constants`, bypassing the conftest redirect that patches it on
  `_kuchnia_config`. The suite was reading the real ~/.config credentials. It
  now goes through the module attribute, which is what the redirect replaces.
  This showed up only as a coverage hole -- the branch was unreachable because
  the real file existed.
- `log_to_credential` guarded `password_hlc is None`, which cannot happen: a
  field's value and clock arrive as one tuple. Dead code, removed rather than
  covered by a contrived test.

The relay canary is a real test now, not a scratch script: a field from a
future release survives merge and re-push untouched, so this is shippable
without a coordinated release. `PASSWORD_FIELD_NAME` is spelled as a join
because ruff's S105 reads the literal as a hardcoded secret and the repo bans
`noqa` outright; the wire name is still "password".

100% branch coverage, 1036 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QQpCiPaSbyRpwsLkH3dgsX
2026-08-23 08:12:30 +02:00

50 lines
1.8 KiB
Python

"""Repo-relative paths every device pushes its state to.
Split out of ``_sync.py`` for file size. These are the wire layout of the
`kuhyx/syncs` repo, so they are shared by the tick that pushes the food log
(:mod:`diet_guard._sync`) and the one that pushes the banks and budget
(:mod:`diet_guard._sync_banks`) -- a second copy of the layout in either
module is how one of them starts writing where the other does not look.
Every device pushes under its own persisted per-install uuid, never a role
constant; see :mod:`diet_guard._device`.
"""
from __future__ import annotations
_DEVICES_DIR = "diet-guard-sync/devices"
#: Where per-device revision markers live, used to skip re-reading an
#: unchanged remote log.
_REVS_DIR = "diet-guard-sync/revs"
def _device_log_path(device_id: str) -> str:
"""Return the repo-relative path a device's full log is pushed to."""
return f"{_DEVICES_DIR}/{device_id}/food_log.json"
def _device_food_bank_path(device_id: str) -> str:
"""Return one device's pushed derived-food-bank path."""
return f"{_DEVICES_DIR}/{device_id}/food_bank.json"
def _device_manual_bank_path(device_id: str) -> str:
"""Return one device's pushed curated-food-bank path."""
return f"{_DEVICES_DIR}/{device_id}/food_bank_manual.json"
def _device_budget_path(device_id: str) -> str:
"""Return the repo-relative path a device's budget is pushed to."""
return f"{_DEVICES_DIR}/{device_id}/budget.json"
def _device_kuchnia_path(device_id: str) -> str:
"""Return one device's pushed catering-credential path.
Its own document rather than a field on ``budget.json``: the budget is
written back at default permissions, and a password riding inside it would
be readable by anything that reads the budget.
"""
return f"{_DEVICES_DIR}/{device_id}/kuchnia.json"