Documentation
¶
Index ¶
- Constants
- func CurrentOwnAddresses(port int) sdkp2p.OwnAddresses
- func PickLive(ctx context.Context, p Picker, id string) (peer.Peer, error)
- type AddrBook
- func (b *AddrBook) ClearLAN(peerId string)
- func (b *AddrBook) ClearTicket(peerId string)
- func (b *AddrBook) HasLAN(peerId string) bool
- func (b *AddrBook) Init(a *app.App) error
- func (b *AddrBook) Name() string
- func (b *AddrBook) SetLAN(peerId string, addrs []string)
- func (b *AddrBook) SetTicket(peerId, ticket string)
- func (b *AddrBook) Ticket(peerId string) string
- type Discovery
- func (d *Discovery) Close(_ context.Context) error
- func (d *Discovery) Enabled() bool
- func (d *Discovery) Init(_ *app.App) error
- func (d *Discovery) Name() string
- func (d *Discovery) Port() int
- func (d *Discovery) Possibility() sdkp2p.Possibility
- func (d *Discovery) RegisterPossibilityHook(fn func(sdkp2p.Possibility))
- func (d *Discovery) Run(_ context.Context) error
- func (d *Discovery) SetEnabled(enabled bool)
- type Exchange
- func (e *Exchange) Broadcast(ctx context.Context)
- func (e *Exchange) Init(a *app.App) error
- func (e *Exchange) Name() string
- func (e *Exchange) PeerDiscovered(ctx context.Context, discovered sdkp2p.DiscoveredPeer, own sdkp2p.OwnAddresses)
- func (e *Exchange) PeerLost(peerId string)
- func (e *Exchange) SetAccountKeysFn(fn func(ctx context.Context, spaceIds []string) map[string][]byte)
- func (e *Exchange) SetKnownSpaceIdsFn(fn func() []string)
- func (e *Exchange) SetOwnAddressesFn(fn func() sdkp2p.OwnAddresses)
- func (e *Exchange) SpaceExchange(_ context.Context, _ *clientspaceproto.SpaceExchangeRequest) (*clientspaceproto.SpaceExchangeResponse, error)
- func (e *Exchange) SpaceExchangeV2(ctx context.Context, req *clientspaceproto.SpaceExchangeV2Request) (*clientspaceproto.SpaceExchangeV2Response, error)
- type Global
- func (g *Global) AccountEnabled() bool
- func (g *Global) Close(_ context.Context) error
- func (g *Global) Init(a *app.App) error
- func (g *Global) LoadedSpaceIds() []string
- func (g *Global) Name() string
- func (g *Global) PeerStatus(peerId string) sdkp2p.PeerStatus
- func (g *Global) Republish(spaceId string)
- func (g *Global) RepublishAccount()
- func (g *Global) Run(_ context.Context) error
- func (g *Global) SetAccount(keys *account.Keys, client accountClient, insecure bool, path string)
- func (g *Global) SetAdvertiseFn(fn func(spaceId string) bool)
- func (g *Global) SetKVSubscriber(fn KVSubscriber)
- func (g *Global) SetOnLive(fn func(peerId string))
- func (g *Global) SpaceLoaded(spaceId string, kv SpaceKV)
- func (g *Global) SpaceUnloaded(spaceId string)
- func (g *Global) Status() sdkp2p.GlobalStatus
- type KVHandler
- type KVSubscriber
- type Notifier
- type Observer
- type PeerRecord
- type PeerStore
- func (p *PeerStore) AccountPeerIds() []string
- func (p *PeerStore) AddObserver(o Observer)
- func (p *PeerStore) AddSourceObserver(o SourceObserver)
- func (p *PeerStore) AllGlobalPeers() []string
- func (p *PeerStore) AllLocalPeers() []string
- func (p *PeerStore) GlobalPeerIds(spaceId string) []string
- func (p *PeerStore) GlobalSpaceIds(peerId string) []string
- func (p *PeerStore) HasAccountPeer(peerId string) bool
- func (p *PeerStore) HasGlobalPeer(peerId string) bool
- func (p *PeerStore) HasLocalPeer(peerId string) bool
- func (p *PeerStore) HasSpace(peerId, spaceId string) bool
- func (p *PeerStore) Init(_ *app.App) error
- func (p *PeerStore) LocalPeerIds(spaceId string) []string
- func (p *PeerStore) Name() string
- func (p *PeerStore) RemoveAccountPeer(peerId string)
- func (p *PeerStore) RemoveGlobalPeer(peerId string)
- func (p *PeerStore) RemoveLocalPeer(peerId string)
- func (p *PeerStore) SetStatus(b *StatusBook)
- func (p *PeerStore) Sources(peerId string) []Source
- func (p *PeerStore) SpaceIds(peerId string) []string
- func (p *PeerStore) UpdateAccountPeer(peerId string)
- func (p *PeerStore) UpdateGlobalPeer(peerId string, spaceIds []string)
- func (p *PeerStore) UpdateLocalPeer(peerId string, spaceIds []string)
- type Picker
- type Source
- type SourceObserver
- type SpaceKV
- type StatusBook
- func (b *StatusBook) Attempt(peerId string, ok bool)
- func (b *StatusBook) Close() error
- func (b *StatusBook) Flush() error
- func (b *StatusBook) Forget(peerId string)
- func (b *StatusBook) Get(peerId string) (PeerRecord, bool)
- func (b *StatusBook) LastSeen(peerId string) time.Time
- func (b *StatusBook) Load() error
- func (b *StatusBook) Seen(peerId string, at time.Time) bool
- func (b *StatusBook) SetOnAdvance(fn func(peerId string))
- func (b *StatusBook) Thresholds() Thresholds
- func (b *StatusBook) Tier(peerId string) Tier
- type Thresholds
- type Tier
Constants ¶
const ( // DefaultServiceType is the DNS-SD service type SDK peers announce. // Deliberately NOT anytype-heart's "_anytype._tcp" — the protocols // are separate networks. DefaultServiceType = "_any._tcp" )
const PickTimeout = 50 * time.Millisecond
PickTimeout bounds a pool.Pick on a global peer. The pool's Pick waits on an in-flight load for the same id (a LAN+global peer can have a LAN dial in flight), so an unbounded ctx would turn "never dials" into "waits for somebody else's dial".
const RecordKey = "p2p/iroh"
RecordKey is the key-value key of a device's global p2p record. The store keeps one row per (key, peer), so every device of a space owns exactly one row: its endpoint ticket, re-set as a heartbeat.
Variables ¶
This section is empty.
Functions ¶
func CurrentOwnAddresses ¶
func CurrentOwnAddresses(port int) sdkp2p.OwnAddresses
CurrentOwnAddresses is this device's announce right now: LAN IPv4s plus the given listen port. Used by the exchange's proactive re-handshake.
Types ¶
type AddrBook ¶
type AddrBook struct {
// contains filtered or unexported fields
}
AddrBook is the only writer of peer addresses into any-sync's peer service. A peer is registered with either its LAN addresses (from the space exchange) or its iroh ticket (from key-value records), never both: a LAN dial that fails must answer in one RTT, not fall through into a relay dial that can take the whole dial timeout. LAN wins while present; the ticket takes over once the LAN entry is cleared (mDNS lost, dial strikes). The one address the book does not own is the push node's (config.Push), registered by the SDK directly: it is neither a LAN nor a global peer and never enters the peer store.
func NewAddrBook ¶
func NewAddrBook() *AddrBook
func (*AddrBook) ClearLAN ¶
ClearLAN forgets a peer's LAN addresses; its ticket, if any, takes over.
func (*AddrBook) ClearTicket ¶
ClearTicket forgets a peer's ticket.
func (*AddrBook) SetLAN ¶
SetLAN registers a peer's LAN addresses (already scheme-prefixed). Empty clears them.
type Discovery ¶
type Discovery struct {
// contains filtered or unexported fields
}
Discovery announces this device on the LAN and browses for other SDK peers, feeding each sighting to the Notifier (the SpaceExchangeV2 handshake).
Concurrency model, deliberately simpler than anytype-heart's:
- one supervisor goroutine runs announce+browse "sessions", restarting them (scoped child context) when the interface set changes or the driver dies — never tearing down the component;
- driver callbacks only enqueue onto a bounded channel;
- one consumer goroutine owns the known-peer map and performs all notifier calls serially.
Nothing is ever reassigned after Run; Close cancels one context and waits (bounded) on one WaitGroup.
func NewDiscovery ¶
func (*Discovery) Enabled ¶
Enabled is the local-discovery switch state: config p2p.localDiscovery at start, SetEnabled afterwards. Off as well while p2p is disabled in config, since discovery never runs then whatever the switch says.
func (*Discovery) Possibility ¶
func (d *Discovery) Possibility() sdkp2p.Possibility
Possibility is the current discovery-possibility state.
func (*Discovery) RegisterPossibilityHook ¶
func (d *Discovery) RegisterPossibilityHook(fn func(sdkp2p.Possibility))
RegisterPossibilityHook adds a callback fired (outside locks) on every possibility change. Used by sync status.
func (*Discovery) SetEnabled ¶
SetEnabled switches mDNS announce and browse on or off at runtime (SDK.SetLocalDiscoveryEnabled has the contract). Off ends a live session at once and keeps the supervisor from starting another; on starts a session as soon as the probe allows, cutting short any retry backoff. A restatement is a no-op.
type Exchange ¶
type Exchange struct {
// contains filtered or unexported fields
}
Exchange runs the SpaceExchangeV2 handshake with discovered local peers: both sides learn each other's dialable addresses and which spaces they SHARE, recorded in the PeerStore. Reuses any-sync's clientspaceproto wire shape.
Peers exchange per-space HMAC tokens keyed by a member-only discovery key and learn only the INTERSECTION of their space sets. A matching token proves the sender's membership, so strangers on the LAN learn nothing, can't track a device across sessions, and can't poison the peer store with spaces they don't hold. Spaces whose discovery key isn't derivable yet (ACL not synced) are skipped until it is.
The ACL-derived key alone dead-locks the offline cold restore: a fresh device of the SAME account knows a space's id (from the synced tech-space index) but can't derive its discovery key before pulling the space — and can't pull over LAN without the key. PROBE tokens break the cycle: for known-but-keyless spaces the caller sends a token keyed by an account-derived key (HKDF of the account signing key — only the account's own devices hold it). The responder answers a probe with a membership proof but records NOTHING: a probe claims interest, not possession, so it must not put the caller into the responder's per-space peer set. The caller records the responder as holding the space and pulls from it; the post-pull re-handshake (storage set change → Broadcast) then advertises the space normally.
The legacy plaintext SpaceExchange v1 is NOT supported: the SDK never calls it, and the inbound handler refuses it — full space-id lists must never leave this device, and there is no fallback an attacker could downgrade to. Pre-v2 peers simply don't pair over LAN.
func NewExchange ¶
func (*Exchange) Broadcast ¶
Broadcast re-runs the handshake with every known local peer. Called when this device's own space set changes (space created, pulled, or deleted) so peers learn the new set promptly instead of on the next discovery resweep.
func (*Exchange) PeerDiscovered ¶
func (e *Exchange) PeerDiscovered(ctx context.Context, discovered sdkp2p.DiscoveredPeer, own sdkp2p.OwnAddresses)
PeerDiscovered is the discovery notifier: register the peer's addresses, dial, and run the handshake. Errors are logged, not returned — discovery re-announces periodically, so a failed attempt retries on the next sighting.
func (*Exchange) PeerLost ¶
PeerLost is the discovery notifier for a peer that left the LAN: its LAN addresses and presence go, so its iroh ticket, if any, takes over. The next sighting re-adds it through PeerDiscovered.
func (*Exchange) SetAccountKeysFn ¶
func (e *Exchange) SetAccountKeysFn(fn func(ctx context.Context, spaceIds []string) map[string][]byte)
SetAccountKeysFn wires the account-derived discovery key source for probe tokens. Set once during app assembly.
func (*Exchange) SetKnownSpaceIdsFn ¶
SetKnownSpaceIdsFn wires the known-space-ids source (the tech-space index) for probe tokens. Set after the SDK layers are up — discovery handshakes may already be running concurrently, hence the atomic; handshakes that run before simply don't probe.
func (*Exchange) SetOwnAddressesFn ¶
func (e *Exchange) SetOwnAddressesFn(fn func() sdkp2p.OwnAddresses)
SetOwnAddressesFn wires the announce source Broadcast embeds in proactive re-handshakes. Set once during app assembly.
func (*Exchange) SpaceExchange ¶
func (e *Exchange) SpaceExchange(_ context.Context, _ *clientspaceproto.SpaceExchangeRequest) (*clientspaceproto.SpaceExchangeResponse, error)
SpaceExchange refuses the legacy plaintext v1 handshake: it would hand our full space-id list to any LAN peer, and serving it at all would give an active attacker a downgrade target. The method exists only because the DRPC service interface requires it.
func (*Exchange) SpaceExchangeV2 ¶
func (e *Exchange) SpaceExchangeV2(ctx context.Context, req *clientspaceproto.SpaceExchangeV2Request) (*clientspaceproto.SpaceExchangeV2Response, error)
SpaceExchangeV2 is the inbound side of the token handshake: compute this device's expected request token for every space it holds a discovery key for, intersect with what the caller sent, and answer with membership proofs for the intersection only — keyed by the caller's nonce, so they can't be precomputed or replayed.
type Global ¶
type Global struct {
// contains filtered or unexported fields
}
Global is the internet-wide p2p layer: it publishes this device's endpoint ticket into every loaded space's key-value store, learns the other members' tickets from the same rows, keeps the peer store / addr book / status book in sync with them, gates inbound connections to known members, and runs the connector that maintains a bounded set of global connections. Everything network-facing happens on the connector; the key-value side never dials.
func NewGlobal ¶
func NewGlobal(cfg config.GlobalP2P, selfPeerId, selfIdentity string, store *PeerStore, status *StatusBook, book *AddrBook) *Global
NewGlobal builds the layer; zero budget fields take their defaults.
func (*Global) AccountEnabled ¶
AccountEnabled reports whether the account layer is on.
func (*Global) Close ¶
Close stops the workers (bounded wait), then flushes and closes the status book.
func (*Global) LoadedSpaceIds ¶
LoadedSpaceIds lists the registered spaces.
func (*Global) PeerStatus ¶
func (g *Global) PeerStatus(peerId string) sdkp2p.PeerStatus
PeerStatus fills the liveness fields of one peer.
func (*Global) RepublishAccount ¶
func (g *Global) RepublishAccount()
RepublishAccount runs a record cycle soon (ticket change, own relay change).
func (*Global) SetAccount ¶
SetAccount turns the account layer on. Set before Run. insecure admits http:// relays named by sibling entries, for test relays; path is where the last decoded record is kept across restarts (empty keeps it in memory only).
func (*Global) SetAdvertiseFn ¶
SetAdvertiseFn gates the per-space row: spaces the fn declines get no row and no heartbeat. Loaded spaces are re-evaluated at once.
func (*Global) SetKVSubscriber ¶
func (g *Global) SetKVSubscriber(fn KVSubscriber)
SetKVSubscriber wires the per-space key-value dispatcher. Set during app assembly, before any space loads.
func (*Global) SetOnLive ¶
SetOnLive registers a callback for every global peer that becomes live (dialed or accepted). Set during app assembly.
func (*Global) SpaceLoaded ¶
SpaceLoaded registers a loaded space: its records are read, its applied writes followed, and the own record published (or re-set when stale). Local-only and guest spaces must not be registered.
func (*Global) SpaceUnloaded ¶
SpaceUnloaded drops a space: its records stop contributing to the peer store, and peers known only through it disappear.
func (*Global) Status ¶
func (g *Global) Status() sdkp2p.GlobalStatus
Status is the debug snapshot of the layer.
type KVHandler ¶
type KVHandler func(decryptor keyvaluestorage.Decryptor, kvs []innerstorage.KeyValue)
KVHandler receives applied key-value writes of one space. Runs on any-sync's apply path — decode and enqueue only.
type KVSubscriber ¶
KVSubscriber registers a KVHandler for a space; the app layer wires its per-space dispatcher here.
type Notifier ¶
type Notifier interface {
PeerDiscovered(ctx context.Context, peer sdkp2p.DiscoveredPeer, own sdkp2p.OwnAddresses)
// PeerLost reports a peer that left the LAN (driver lost event).
PeerLost(peerId string)
}
Notifier consumes discovery results; implemented by Exchange.
type Observer ¶
Observer is notified after a peer's space set changes (union over sources). before and after are the peer's space ids around the change; removed is true when the peer is gone from every source (after is then nil). Called outside the store's lock — observers may call back into the store.
type PeerRecord ¶
type PeerRecord struct {
// LastSeen is the newest evidence the peer is alive: a key-value
// heartbeat (publisher clock, clamped to now) or a local
// connection.
LastSeen time.Time `json:"lastSeen"`
// LastAttempt is the last global dial attempt.
LastAttempt time.Time `json:"lastAttempt,omitempty"`
// Failures counts consecutive failed global dials; a success or
// fresh evidence resets it.
Failures int `json:"failures,omitempty"`
}
PeerRecord is the persisted liveness record of one peer.
type PeerStore ¶
type PeerStore struct {
// contains filtered or unexported fields
}
PeerStore tracks the peers that SHARE spaces with this device and which spaces, per source: LAN peers from the space exchange, global peers from key-value records, account peers from the account's discovery record. The per-space peer manager, pubsub and the files p2p source read LAN and global peers separately — LAN peers are dialed inline, global peers are only used while already connected. Account peers are global peers of every space: they carry no space set, GlobalPeerIds lists them for any space asked. In-memory; the LAN side is rediscovered from scratch on restart, the others are rebuilt from the records.
func NewPeerStore ¶
func NewPeerStore() *PeerStore
func (*PeerStore) AccountPeerIds ¶
AccountPeerIds returns the devices of this account known through the record, best first.
func (*PeerStore) AddObserver ¶
AddObserver registers a change callback. No removal — the observer set is fixed at wiring time and lives as long as the app.
func (*PeerStore) AddSourceObserver ¶
func (p *PeerStore) AddSourceObserver(o SourceObserver)
AddSourceObserver registers a per-source presence callback.
func (*PeerStore) AllGlobalPeers ¶
AllGlobalPeers returns every peer known through records — space rows or the account record — most recently seen first, disabled tier excluded.
func (*PeerStore) AllLocalPeers ¶
AllLocalPeers returns every known LAN peer id, sorted.
func (*PeerStore) GlobalPeerIds ¶
GlobalPeerIds returns the global peers known to have spaceId — the space's record peers plus every device of this account — most recently seen first, disabled tier excluded.
func (*PeerStore) GlobalSpaceIds ¶
GlobalSpaceIds returns the spaces a global peer is known to have.
func (*PeerStore) HasAccountPeer ¶
HasAccountPeer reports whether the peer is a device of this account.
func (*PeerStore) HasGlobalPeer ¶
HasGlobalPeer reports whether the peer is known through records — a space row or the account record — in any tier.
func (*PeerStore) HasLocalPeer ¶
HasLocalPeer reports whether the peer is known on the LAN.
func (*PeerStore) HasSpace ¶
HasSpace reports whether the peer is known to hold spaceId through any source; a device of this account holds every space.
func (*PeerStore) LocalPeerIds ¶
LocalPeerIds returns the LAN peers known to have spaceId.
func (*PeerStore) RemoveAccountPeer ¶
RemoveAccountPeer forgets a device of this account.
func (*PeerStore) RemoveGlobalPeer ¶
RemoveGlobalPeer forgets a peer's global presence.
func (*PeerStore) RemoveLocalPeer ¶
RemoveLocalPeer forgets a peer's LAN presence (dial failure, connection closed and gone). Unknown peers are a no-op.
func (*PeerStore) SetStatus ¶
func (p *PeerStore) SetStatus(b *StatusBook)
SetStatus wires the liveness book that orders global peers and hides disabled ones. nil keeps insertion order and hides nobody.
func (*PeerStore) SpaceIds ¶
SpaceIds returns the spaces a peer is known to have, over every source.
func (*PeerStore) UpdateAccountPeer ¶
UpdateAccountPeer records a device of this account. It needs no space set: it holds every space this device holds.
func (*PeerStore) UpdateGlobalPeer ¶
UpdateGlobalPeer records the full global space set for a peer.
func (*PeerStore) UpdateLocalPeer ¶
UpdateLocalPeer records the full LAN space set for a peer; an empty set keeps the peer known (it stays in AllLocalPeers).
type Source ¶
type Source uint8
Source is how a peer became known.
const ( // SourceLAN — the SpaceExchangeV2 handshake on the local network. SourceLAN Source = iota // SourceGlobal — a key-value record in a shared space. SourceGlobal // SourceAccount — the account's device-discovery record: another // device of this account, which holds every space this device holds. SourceAccount )
type SourceObserver ¶
SourceObserver is notified when a peer enters (present) or leaves a source. Called outside the store's lock.
type SpaceKV ¶
type SpaceKV interface {
Store() keyvaluestorage.Storage
// CanWrite reports whether this device may Set rows (writer+).
CanWrite() bool
// IsMember reports whether identity (account address) still holds
// any permission in the space.
IsMember(identity string) bool
}
SpaceKV is the slice of a loaded space the global layer reads and writes: its default key-value store and two ACL answers.
type StatusBook ¶
type StatusBook struct {
// contains filtered or unexported fields
}
StatusBook keeps every known peer's liveness record, persisted as one JSON file under DataDir (same low-ceremony persistence as the p2p port file), written debounced. Records outlive restarts so a device that was offline for weeks resumes with the right tiers instead of dialing every stale peer at boot.
func NewStatusBook ¶
func NewStatusBook(path string, th Thresholds) *StatusBook
NewStatusBook creates a book persisted at path; empty path keeps it in memory only.
func (*StatusBook) Attempt ¶
func (b *StatusBook) Attempt(peerId string, ok bool)
Attempt records the outcome of a global dial: success is liveness evidence and clears the failure streak; failure extends it.
func (*StatusBook) Close ¶
func (b *StatusBook) Close() error
Close flushes pending changes, then refuses further saves.
func (*StatusBook) Flush ¶
func (b *StatusBook) Flush() error
Flush writes the book now (fsync, atomic replace). No-op when clean. The book stays dirty until the replace succeeded, so a failed write is retried by the next change.
func (*StatusBook) Forget ¶
func (b *StatusBook) Forget(peerId string)
Forget drops a peer's record.
func (*StatusBook) Get ¶
func (b *StatusBook) Get(peerId string) (PeerRecord, bool)
Get returns a peer's record.
func (*StatusBook) LastSeen ¶
func (b *StatusBook) LastSeen(peerId string) time.Time
LastSeen returns a peer's LastSeen, zero when unknown.
func (*StatusBook) Load ¶
func (b *StatusBook) Load() error
Load reads the persisted records; a missing file is an empty book. Records past the disable threshold are dropped: the peer is disabled anyway and a fresh row re-creates its record.
func (*StatusBook) Seen ¶
func (b *StatusBook) Seen(peerId string, at time.Time) bool
Seen records liveness evidence at time at. Publisher clocks are untrusted: at is clamped to now. Reports whether LastSeen advanced.
func (*StatusBook) SetOnAdvance ¶
func (b *StatusBook) SetOnAdvance(fn func(peerId string))
SetOnAdvance installs the LastSeen-advanced hook. Set at wiring time.
func (*StatusBook) Thresholds ¶
func (b *StatusBook) Thresholds() Thresholds
Thresholds returns the configured tier boundaries.
func (*StatusBook) Tier ¶
func (b *StatusBook) Tier(peerId string) Tier
Tier classifies a peer now. Unknown peers are disabled: every global peer gets a Seen call from its key-value record before it is used.
type Thresholds ¶
Thresholds are the tier boundaries on the age of a peer's LastSeen.
func ThresholdsFrom ¶
func ThresholdsFrom(g config.GlobalP2P) Thresholds
ThresholdsFrom reads the tier boundaries from the global config (defaults already applied by WithDefaults).
type Tier ¶
type Tier uint8
Tier is a peer's liveness class, derived from how long ago it was last seen. It sets how eagerly the connector dials the peer and whether the peer is offered to sync at all.
const ( // TierActive — seen recently; kept connected with a short backoff. TierActive Tier = iota // TierStale — probed on a slow cadence, after active peers. TierStale // TierDormant — probed at startup and every few hours. TierDormant // TierDisabled — never dialed, dropped from the addr book and the // inbound allowlist until fresh evidence arrives. TierDisabled )
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package account is the account-level device-discovery record: every device of an account registers itself in one pkarr record addressed by a key derived from the identity key, and every device — a fresh restore included — resolves its siblings from it.
|
Package account is the account-level device-discovery record: every device of an account registers itself in one pkarr record addressed by a key derived from the identity key, and every device — a fresh restore included — resolves its siblings from it. |