Each daemon writes to <data-dir>/messages.db on startup. Messages received or sent are stored immediately; duplicate mids (INSERT OR IGNORE) are safe to call multiple times. Peer aliases are upserted on peer_connected and again after hello verification when the real nick is known. Schema - messages(mid UNIQUE, room, from_peer, body, sent_at) — mid is the YAW/2 dedup key added in the proto migration; index on (room, sent_at) for efficient per-room queries. - peers(peer_id PK, alias, last_seen) — cache of every peer ever seen, used to resolve hex ids to names when peers are offline. Wiring - store.Open called in cmd/daemon/main.go, passed to mesh.New. - mesh.Mesh holds *store.Store (nil-safe; persistence is optional). - mesh.SaveMessage called in dispatchPeerMessage (incoming) and ipc CmdSendMessage (outgoing) so the local node's own messages are stored. - mesh.UpdatePeerAlias called after hello verification updates the alias with the verified nick rather than the placeholder short-id. Messages only accumulate from join time forward — no history replay to late-joining peers; each node's view starts from when it connected. test-network.sh: added SQLite verification block that queries each node's DB after the test and prints message + peer counts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
68 lines
1.9 KiB
Go
68 lines
1.9 KiB
Go
// waste-daemon: the local peer process.
|
|
// Run one of these on each friend's machine.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/waste-go/internal/anchor"
|
|
"github.com/waste-go/internal/crypto"
|
|
"github.com/waste-go/internal/ipc"
|
|
"github.com/waste-go/internal/mesh"
|
|
"github.com/waste-go/internal/proto"
|
|
"github.com/waste-go/internal/store"
|
|
)
|
|
|
|
func main() {
|
|
dataDir := flag.String("data-dir", "~/.waste", "path to identity/config directory")
|
|
alias := flag.String("alias", "anon", "display name shown to peers (advisory only)")
|
|
ipcPort := flag.Int("ipc-port", 17337, "port for local IPC (UI connects here)")
|
|
anchorURL := flag.String("anchor", "", "anchor WebSocket URL, e.g. ws://your-vps:17339/ws")
|
|
flag.Parse()
|
|
|
|
dir := expandHome(*dataDir)
|
|
id, err := crypto.LoadOrCreate(dir, *alias)
|
|
if err != nil {
|
|
log.Fatalf("identity: %v", err)
|
|
}
|
|
log.Printf("daemon: local peer id: %s alias: %s", id.PeerID().Short(), id.Alias)
|
|
|
|
st, err := store.Open(filepath.Join(dir, "messages.db"))
|
|
if err != nil {
|
|
log.Fatalf("store: %v", err)
|
|
}
|
|
defer st.Close()
|
|
|
|
m := mesh.New(id, st)
|
|
|
|
// joinFn is passed to the IPC layer; it's called when the UI sends join_network.
|
|
joinFn := func(ctx context.Context, networkName string) {
|
|
if *anchorURL == "" {
|
|
log.Printf("daemon: join_network: no -anchor flag set")
|
|
m.Emit(proto.IpcMessage{Type: proto.EvtError, ErrorMessage: "no anchor configured — start daemon with -anchor <url>"})
|
|
return
|
|
}
|
|
log.Printf("daemon: joining network %q via %s", networkName, *anchorURL)
|
|
anchor.Run(ctx, *anchorURL, networkName, id, m)
|
|
log.Printf("daemon: left network %q", networkName)
|
|
}
|
|
|
|
if err := ipc.Run(m, *ipcPort, joinFn); err != nil {
|
|
log.Fatalf("ipc: %v", err)
|
|
}
|
|
}
|
|
|
|
func expandHome(path string) string {
|
|
if len(path) >= 2 && path[:2] == "~/" {
|
|
if home, err := os.UserHomeDir(); err == nil {
|
|
return home + path[1:]
|
|
}
|
|
}
|
|
return path
|
|
}
|
|
|