Documentation
¶
Overview ¶
Package edgesync is the generic poll/report rails behind every edge sync channel — edge packs, local controls, and whatever comes after. Before this package, config_poll.go's heartbeat loop was the only precedent, and it is entirely Guard-specific (one hardcoded path, one fixed response struct, fields owned directly by Guard) — not something a second, unrelated sync channel could reuse without copying its shape wholesale. Syncer factors that shape out: a signed poll on an interval, change detection by content hash, a callback fired only on real change, and an optional signed report call — so a second sync channel is a few lines of wiring, not a copy of the first.
Syncer does not know or care what it is syncing. It moves bytes; the caller (edge-pack sync, local-controls sync) owns decoding, converging local state, and deciding what to report.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Option ¶
type Option func(*Syncer)
Option configures a Syncer at construction.
func WithHeaders ¶
WithHeaders attaches extra headers (e.g. hostname, a locally-tracked hash) to every poll GET, mirroring config_poll.go's heartbeatHeaders.
func WithOnUpdate ¶
WithOnUpdate sets the callback fired with the raw response body whenever a poll's content differs from the last one seen. Optional — a Syncer with no OnUpdate is a poll-and-discard heartbeat (rarely useful) or, combined with WithReportBuilder alone, a report-only channel.
func WithReportBuilder ¶
WithReportBuilder causes the poll loop to also report on every tick, regardless of whether the poll response changed: after each successful poll, onReport builds the current local state and Syncer POSTs it to the report path. Errors from onReport or the POST are swallowed — same best-effort posture as the poll itself. Requires WithReportPath.
func WithReportPath ¶
WithReportPath sets the signed POST endpoint Report (and, if WithReportBuilder is also set, the per-tick auto-report) targets. A Syncer with no report path is poll-only — Report returns an error, and a report builder is never invoked.
type Syncer ¶
type Syncer struct {
// contains filtered or unexported fields
}
Syncer polls a signed endpoint on an interval and invokes OnUpdate only when the raw response content actually changes (sha256 comparison) — a healthy steady-state poll that returns unchanged bytes is silent. It can also report local state back over a second signed endpoint, either per-tick (WithReportBuilder, mirrors today's "the pull doubles as the report" cadence) or on demand (Report, for reporting immediately after a local convergence rather than waiting for the next tick).
Poll errors are non-fatal — this is config distribution, not enforcement; a failed tick just retries next interval, the same philosophy as the existing heartbeat poller.
func New ¶
New builds a Syncer. pollPath is the signed GET endpoint to watch for change; interval is the poll cadence. This package imposes no minimum interval — callers own that policy (flyedged's FLYEDGED_PACK_INTERVAL floor, for example).
func (*Syncer) Poll ¶
Poll does one signed GET and reports whether the content changed since the last call this Syncer made (sha256 of the raw bytes). Exported so a caller wanting manual control — a one-shot check rather than a background loop — never needs Start/Stop at all.
When the Transport supports conditional GETs, the last-seen hash goes out as If-None-Match and an unchanged response comes back as a bodiless 304 — so a steady-state poll transfers nothing. A 304 returns (nil, false, nil): no bytes and no change, which is exactly what the caller already handles, since it only acts on changed == true.
func (*Syncer) Report ¶
Report signs and POSTs body to the configured report path. Returns an error if no report path was set via WithReportPath — reporting is opt-in per channel.
type Transport ¶
type Transport interface {
GetSigned(ctx context.Context, path string, headers map[string]string) ([]byte, error)
PostSigned(ctx context.Context, path string, body []byte) ([]byte, error)
}
Transport is the signed HTTP capability a Syncer needs. *enforce.HTTPEnforcer already implements this (GetSigned/PostSigned are exported precisely because config_poll.go's signedGetter/connect.go's signedPoster seams needed them) — no new signing code, no new dependency. A test stub can substitute a fake satisfying the same two methods.