What happens
addresses.source reads as the origin of an address — its values are generated, icloud, inbox, and manual — but it is overwritten on every write rather than kept from the first one.
upsert_address assigns it unconditionally in its conflict clause (src/hidemyemail_generator/inbox.py:165):
ON CONFLICT(email) DO UPDATE SET
...
source = excluded.source,
and three callers pass a different value for the same address over its lifetime:
| Caller |
Value passed |
_generate — src/hidemyemail_generator/main.py:1219 |
generated |
_sync_hme_to_db — src/hidemyemail_generator/main.py:1383 |
icloud |
insert_message — src/hidemyemail_generator/inbox.py:390 |
inbox |
Whichever ran most recently wins, so the column ends up describing the last thing that touched the row.
Reproduction
hidemyemail generate --label test --count 1 → the new row has source = generated
hidemyemail inbox sync-hme → the same row now has source = icloud
- Receive mail at that address and sync the inbox → it becomes
source = inbox
Observed on a real account: after one sync-hme, all 748 addresses read icloud regardless of how each was actually created. A freshly generated address held generated for under a minute before the next sync overwrote it.
Impact
source is surfaced in hidemyemail inbox addresses table output, in its --result-json payload, and as a column in the addresses.csv export. Consumers of any of those get "last writer" rather than provenance, and the original value is not recoverable once overwritten.
For contrast, state in the same conflict clause is explicitly protected against being clobbered:
state = CASE
WHEN addresses.state = 'unused' THEN excluded.state
ELSE addresses.state
END,
so the two adjacent fields currently follow different merge rules.
What happens
addresses.sourcereads as the origin of an address — its values aregenerated,icloud,inbox, andmanual— but it is overwritten on every write rather than kept from the first one.upsert_addressassigns it unconditionally in its conflict clause (src/hidemyemail_generator/inbox.py:165):and three callers pass a different value for the same address over its lifetime:
_generate—src/hidemyemail_generator/main.py:1219generated_sync_hme_to_db—src/hidemyemail_generator/main.py:1383icloudinsert_message—src/hidemyemail_generator/inbox.py:390inboxWhichever ran most recently wins, so the column ends up describing the last thing that touched the row.
Reproduction
hidemyemail generate --label test --count 1→ the new row hassource = generatedhidemyemail inbox sync-hme→ the same row now hassource = icloudsource = inboxObserved on a real account: after one
sync-hme, all 748 addresses readicloudregardless of how each was actually created. A freshly generated address heldgeneratedfor under a minute before the next sync overwrote it.Impact
sourceis surfaced inhidemyemail inbox addressestable output, in its--result-jsonpayload, and as a column in theaddresses.csvexport. Consumers of any of those get "last writer" rather than provenance, and the original value is not recoverable once overwritten.For contrast,
statein the same conflict clause is explicitly protected against being clobbered:so the two adjacent fields currently follow different merge rules.