Real sync layer back, now backed by a persistent p2panda-store SqliteStore
at $DATA_DIR/sync_log.db (separate from interpretations.db so we don't have
to share schemas with the rusqlite-era zodia-store rewrite).
API churn handled:
- MemoryStore → SqliteStore (persistent; LogStore + TopicStore impls come
from p2panda-store, no custom TopicMap needed anymore)
- LogSync::builder(store, topic_map, endpoint, gossip) → builder(store, endpoint, gossip)
- TopicLogSyncEvent::Operation(op) → ::OperationReceived { operation, metrics }
- TopicLogSyncEvent::SyncStarted(_) → ::SyncStarted { metrics }
- TopicLogSyncEvent::SyncFinished(_) → ::SyncFinished { metrics }
- Header.public_key → verifying_key; Header.previous field removed
- Header.timestamp now Timestamp (use Timestamp::now())
- get_latest_entry / insert_operation now live on LogStore/OperationStore traits
Side effect: offline-catch-up of community interpretations is back online.
ZodiaSyncNode::spawn now requires the data_dir path so it can place its log
DB alongside the main store; caller updated.
API churn handled:
- p2panda_core::PrivateKey → SigningKey, PublicKey → VerifyingKey
- iroh_endpoint::Builder::private_key() → .signing_key()
- gossip.stream(): now takes Topic instead of [u8; 32] — use Topic::from(key.0)
- p2panda-core 0.6 in zodia-crypto + zodia-app
zodia-sync is stubbed: the new LogSync API (SqliteStore-backed, TopicStore
trait, restructured FromSync events) needs a real rewrite that I'm deferring
to Phase 3. The stub exposes the same public surface (spawn/publish/
ReceivedInterp) but performs no actual sync, so live Tier-1 direct exchange
still works while offline catch-up of community interpretations is paused.
Workspace builds clean. Smoke-test before piling Phase 3 on top.
Convert AspectView to SimpleAsyncComponent so init+update can await the
async store. Affirm/submit clicks route through AppMsg::AffirmInterp /
AppMsg::SubmitInterp so the parent handles the actual store write on its
own runtime — the click closure just dispatches the message and updates
the UI optimistically.
Drop the dangerous _blocking helpers on ZodiaStore (would have panicked
on GTK main thread since GTK callbacks run outside tokio context). Last
sync callsite — the chat-history preload in AppModel::init — now uses
direct .await inside the async init.
Workspace builds clean. Phase 1 (storage migration) is done; rusqlite is
gone, sqlx-sqlite is the only SQLite linker, p2panda 0.6's libsqlite3-sys
dep no longer conflicts. Next: Phase 2 — bump iroh 0.96 → 0.98 + p2panda
0.5 → 0.6, then Phase 3 (replace ZodiaSyncNode with the new Node API).
Migrate zodia-store from rusqlite to sqlx-sqlite to unblock p2panda 0.6
(which also links sqlite3 via sqlx, conflicting with rusqlite's libsqlite3-sys).
zodia-store and zodia-sync compile cleanly; ~10 build errors remain in
app/src/aspect_view.rs and stargazer_page.rs where sync GTK widget init +
click closures need to read/write the now-async store. Next step is to
pre-fetch in async parent (AppModel) and pass plain data into the sync
SimpleComponent — not yet done.
WIP checkpoint on port/p2panda-node-api; do not merge to main.
The self-hosted iroh-relay at stargaze.whatdoyouliketodo.com was no longer
reachable (Caddy up, iroh-relay daemon down), producing 'Failed to connect
to relay server' warnings on every startup. Remove RELAY_ZODIA,
ALL_RELAYS, and BOOTSTRAP_NODE_ID and let iroh use its public default
relays. Delete the zodia-bootstrap binary and service unit — no longer
needed.
build_setup_page: declarative toolbar/header/date_group/city_group/manual_group/
content via relm4::view!; SpinRow construction extracted to spin_row helper.
build_main_page: nav row factory function uses view!; consent_bar and call_bar
fully declarative; chart/sky/network tab toolbars deduplicated via new
make_tab_toolbar(title, body) helper.
stargazer_page::build_messages_tab: scrolled list + input row tree expressed
in view!; signal wiring stays imperative below.
Net minor LOC growth — the win is structural clarity over imperative threading,
not size.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
InterpKey gains PlacementSign { planet, sign } and PlacementHouse
{ planet, house } variants with canonical sigs placement_sign:planet:sign
and placement_house:planet:house. plain_name renders "Jupiter in Virgo"
and "Jupiter in House 9" respectively.
placements.rs now produces a Vec<AspectItem> rather than rendering a
static PreferencesGroup; AspectView's natal kind builds a second
FactoryVecDeque<InterpRow> above the aspects group titled "Placements"
that feeds through the same Activate forwarding so taps push the same
detail_page used by aspects.
Baseline TOML gains [placement_sign] and [placement_house] sections
seeded with a curated subset spanning planets / signs / houses.
format_interp_key in the network feed and store kind_str learn the new
variants.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
HashMap<PeerId, Stargazer> backed by a StargazerState enum:
Discovered -> OutgoingPending -> Connected
Discovered -> IncomingPending -> Connected
Key behaviours:
- OutgoingPending persisted in pending.tsv; retried automatically on
every PeerDiscovered event and on NetworkReady startup
- Mutual pending fast-path: if both sides have proposed, the incoming
channel is auto-accepted without showing the consent bar
- Sidebar "Others" section shows all non-Discovered peers sorted:
Connected+online, IncomingPending, OutgoingPending, Connected+offline
- Pending rows are non-navigable with a visible x remove button
- Connected rows retain the nickname pencil and unread badge
- Outgoing consent bar removed; + immediately commits to OutgoingPending
- Desktop notifications unchanged (consent bar still fires on IncomingPending)
- Dead peer_list.rs removed