Documentation
¶
Index ¶
- Constants
- Variables
- func ChatAddressString(addr [protocol.AddressSize]byte) string
- func DecompressMediaReader(r io.Reader, compression protocol.MediaCompression) (io.ReadCloser, error)
- func ParseChatAddress(s string) ([protocol.AddressSize]byte, error)
- type Cache
- func (c *Cache) Cleanup() error
- func (c *Cache) GetAllTitles() map[string]string
- func (c *Cache) GetMessages(channelName string) *MessagesResult
- func (c *Cache) MergeAndPut(channelName string, fresh []protocol.Message) (*MessagesResult, error)
- func (c *Cache) PutMetadata(meta *protocol.Metadata) error
- func (c *Cache) PutTitle(channelName, title string) error
- type ChatClient
- func (c *ChatClient) Ack(ctx context.Context, peer [protocol.AddressSize]byte, upToSeq uint32) error
- func (c *ChatClient) EnsureInfo(ctx context.Context) (*protocol.ChatInfo, error)
- func (c *ChatClient) FetchInbox(ctx context.Context, onQuery ChatProgress) ([]ChatIncoming, error)
- func (c *ChatClient) FetchPeerKey(ctx context.Context, addr [protocol.AddressSize]byte) (*protocol.RegisterEnvelope, error)
- func (c *ChatClient) Identity() *ChatIdentity
- func (c *ChatClient) IsRegistered(ctx context.Context, peer [protocol.AddressSize]byte) (bool, error)
- func (c *ChatClient) NextSeq(ctx context.Context, peer [protocol.AddressSize]byte) (uint32, error)
- func (c *ChatClient) PeerStatus(ctx context.Context, peer [protocol.AddressSize]byte) (accepted, delivered uint32, err error)
- func (c *ChatClient) Quota() (remaining uint16, resetUnix uint32, known bool)
- func (c *ChatClient) Register(ctx context.Context, _ ChatProgress) error
- func (c *ChatClient) Registered() bool
- func (c *ChatClient) SendMessage(ctx context.Context, peer [protocol.AddressSize]byte, seq uint32, text string, ...) (*ChatSendResult, error)
- func (c *ChatClient) ServerAdvertisesChat(ctx context.Context) (advertises, known bool)
- func (c *ChatClient) SetBudget(b int)
- type ChatIdentity
- type ChatIncoming
- type ChatProgress
- type ChatSendResult
- type ChatStatusError
- type Fetcher
- func (f *Fetcher) AllResolvers() []string
- func (f *Fetcher) ExportStats() map[string][3]int64
- func (f *Fetcher) FetchBlock(ctx context.Context, channel, block uint16) ([]byte, error)
- func (f *Fetcher) FetchChannel(ctx context.Context, channelNum int, blockCount int) ([]protocol.Message, error)
- func (f *Fetcher) FetchChannelVerified(ctx context.Context, channelNum int, blockCount int, expectedHash uint32) ([]protocol.Message, error)
- func (f *Fetcher) FetchLatestVersion(ctx context.Context) (string, error)
- func (f *Fetcher) FetchMedia(ctx context.Context, channel uint16, blockCount uint16, expectedCRC32 uint32, ...) ([]byte, error)
- func (f *Fetcher) FetchMediaBlocksStream(ctx context.Context, channel, startBlock, count uint16, w io.Writer, ...) error
- func (f *Fetcher) FetchMetadata(ctx context.Context) (*protocol.Metadata, error)
- func (f *Fetcher) FetchProfilePicDirectory(ctx context.Context) (ProfilePicsBundle, error)
- func (f *Fetcher) FetchRelayInfo(ctx context.Context) (RelayInfo, error)
- func (f *Fetcher) FetchTitles(ctx context.Context) (map[string]string, error)
- func (f *Fetcher) HasServerKey() bool
- func (f *Fetcher) ImportStats(m map[string][3]int64)
- func (f *Fetcher) QueryMode() protocol.QueryEncoding
- func (f *Fetcher) QueryTotals() (responses, errs int64)
- func (f *Fetcher) RecordFailure(resolver string)
- func (f *Fetcher) RecordSuccess(resolver string, latency time.Duration)
- func (f *Fetcher) RemoveActiveResolver(addr string)
- func (f *Fetcher) ResetStats()
- func (f *Fetcher) ResolverScoreboard() []ResolverInfo
- func (f *Fetcher) Resolvers() []string
- func (f *Fetcher) ScanConcurrency() int
- func (f *Fetcher) SendAdminCommand(ctx context.Context, cmd protocol.AdminCmd, arg string) (string, error)
- func (f *Fetcher) SendMessage(ctx context.Context, channelNum int, text string) error
- func (f *Fetcher) SetActiveResolvers(resolvers []string)
- func (f *Fetcher) SetCacheEpoch(epoch uint32)
- func (f *Fetcher) SetCacheShare(on bool)
- func (f *Fetcher) SetDebug(debug bool)
- func (f *Fetcher) SetDomains(extra []string)
- func (f *Fetcher) SetLogFunc(fn LogFunc)
- func (f *Fetcher) SetNoiseDisabled(v bool)
- func (f *Fetcher) SetQueryMode(mode protocol.QueryEncoding)
- func (f *Fetcher) SetRateLimit(qps float64)
- func (f *Fetcher) SetResolvers(resolvers []string)
- func (f *Fetcher) SetScatter(n int)
- func (f *Fetcher) SetServerPublicKey(b64 string) error
- func (f *Fetcher) SetStatsForward(fn func(resolver string, ok bool, latency time.Duration))
- func (f *Fetcher) SetTimeout(d time.Duration)
- func (f *Fetcher) Start(ctx context.Context)
- func (f *Fetcher) UpdateResolverPool(resolvers []string)
- type Gap
- type LogFunc
- type MediaProgress
- type MessagesResult
- type ProfilePicEntry
- type ProfilePicsBundle
- type RelayInfo
- type ResolverChecker
- func (rc *ResolverChecker) CancelCurrentScan()
- func (rc *ResolverChecker) CheckNow(ctx context.Context) bool
- func (rc *ResolverChecker) SetAutoScan(enabled bool)
- func (rc *ResolverChecker) SetLogFunc(fn LogFunc)
- func (rc *ResolverChecker) SetOnScanDone(fn func([]string))
- func (rc *ResolverChecker) Start(ctx context.Context)
- func (rc *ResolverChecker) StartAndNotify(ctx context.Context, onFirstDone func())
- func (rc *ResolverChecker) StartPeriodic(ctx context.Context)
- type ResolverInfo
- type ResolverScanner
- func (rs *ResolverScanner) Pause()
- func (rs *ResolverScanner) Progress() ScannerProgress
- func (rs *ResolverScanner) Resume()
- func (rs *ResolverScanner) SetDebug(v bool)
- func (rs *ResolverScanner) SetLogFunc(fn LogFunc)
- func (rs *ResolverScanner) Start(cfg ScannerConfig) error
- func (rs *ResolverScanner) State() ScannerState
- func (rs *ResolverScanner) Stop()
- type ScannerConfig
- type ScannerProgress
- type ScannerResult
- type ScannerState
Constants ¶
const MediaBlockHeaderLen = protocol.MediaBlockHeaderLen
MediaBlockHeaderLen re-exports the protocol header length so callers in the web layer don't have to import the protocol package twice.
Variables ¶
var ( ErrChatNoServerKey = errors.New("chat requires a pinned server key (sk)") ErrChatDisabled = errors.New("chat not enabled on this server") ErrChatServerDisabled = errors.New("chat is turned off by this server") ErrChatUnverified = errors.New("chat info signature missing or invalid") // ErrChatVersion means the server speaks a chat protocol this client can't // (e.g. an old server still on the previous wire) — a stable mismatch, so it // is backed off long and never retried in a tight loop. ErrChatVersion = errors.New("chat protocol version incompatible") // ErrChatUnreachable means a transport/handshake failure (e.g. the server // is rebooting) — transient, retry — not "no chat here". ErrChatUnreachable = errors.New("chat server unreachable") )
Chat errors. ChatStatusError carries a server-reported status.
var ErrContentHashMismatch = fmt.Errorf("content hash mismatch")
ErrContentHashMismatch is returned when the fetched messages do not match the expected content hash from metadata. This typically means the server regenerated its blocks between the metadata fetch and the block fetch (block-version race). The caller should re-fetch metadata and retry.
var ErrExtraBlockInvalid = errors.New("content signature invalid")
ErrExtraBlockInvalid means an ExtraBlock WAS returned but failed verification (bad signature, wrong channel, rolled-back timestamp, or content-digest mismatch) — i.e. an active tamper attempt. Always hard-fail.
var ErrMediaHashMismatch = fmt.Errorf("media content hash mismatch")
ErrMediaHashMismatch indicates the assembled bytes don't match the expected CRC32. The caller must discard the returned bytes.
var ErrUnverified = errors.New("content unverified: no signature block")
ErrUnverified means no valid ExtraBlock could be fetched (e.g. an old server that doesn't emit one). Content integrity could not be checked; callers decide policy (feed: accept with a warning; messenger: reject).
Functions ¶
func ChatAddressString ¶ added in v0.25.9
func ChatAddressString(addr [protocol.AddressSize]byte) string
ChatAddressString renders an address for sharing (lowercase base32, 20 chars).
func DecompressMediaReader ¶ added in v0.13.0
func DecompressMediaReader(r io.Reader, compression protocol.MediaCompression) (io.ReadCloser, error)
DecompressMediaReader wraps r per the given compression.
func ParseChatAddress ¶ added in v0.25.9
func ParseChatAddress(s string) ([protocol.AddressSize]byte, error)
ParseChatAddress parses a shared address string.
Types ¶
type Cache ¶
type Cache struct {
// contains filtered or unexported fields
}
Cache stores channel and metadata snapshots on disk, keyed by channel name. Channel files not updated for 7 days are automatically removed by Cleanup.
func (*Cache) Cleanup ¶ added in v0.5.1
Cleanup removes channel cache files (ch_*.json) not modified in 7 days.
func (*Cache) GetAllTitles ¶ added in v0.11.0
GetAllTitles reads the display name from every ch_*.json cache file and returns a map of original channel name → display name. Files without a stored name or display name are skipped silently.
func (*Cache) GetMessages ¶
func (c *Cache) GetMessages(channelName string) *MessagesResult
GetMessages reads the cached message history for a channel by name. Returns nil if the file is missing or has not been updated within 7 days.
func (*Cache) MergeAndPut ¶ added in v0.5.1
MergeAndPut merges fresh messages with the on-disk history, enforces the 200-message cap, detects gaps, persists the result, and returns it. Existing history is always included regardless of age to preserve context.
func (*Cache) PutMetadata ¶
PutMetadata stores metadata.
type ChatClient ¶ added in v0.25.9
type ChatClient struct {
// OnHandshake, if set, reports handshake cell progress (done, total).
OnHandshake ChatProgress
// contains filtered or unexported fields
}
ChatClient drives the chat protocol for one identity on one server config.
func NewChatClient ¶ added in v0.25.9
func NewChatClient(f *Fetcher, id *ChatIdentity) *ChatClient
NewChatClient creates a chat driver bound to a fetcher and identity.
func (*ChatClient) Ack ¶ added in v0.25.9
func (c *ChatClient) Ack(ctx context.Context, peer [protocol.AddressSize]byte, upToSeq uint32) error
Ack confirms delivery of peer's messages up to upToSeq, freeing inbox quota and driving the sender's ✓✓.
func (*ChatClient) EnsureInfo ¶ added in v0.25.9
EnsureInfo fetches and verifies the ChatInfo capability payload once (and re-fetches after expiry or when marked stale). Fail-closed: chat requires a pinned sk and a VERIFIED signature.
func (*ChatClient) FetchInbox ¶ added in v0.25.9
func (c *ChatClient) FetchInbox(ctx context.Context, onQuery ChatProgress) ([]ChatIncoming, error)
FetchInbox polls the inbox and decrypts waiting messages. A message that fails transiently (network, sender key not yet propagated) is withheld along with any later message from the same sender, so the caller never acks past a seq that's still on the server.
func (*ChatClient) FetchPeerKey ¶ added in v0.25.9
func (c *ChatClient) FetchPeerKey(ctx context.Context, addr [protocol.AddressSize]byte) (*protocol.RegisterEnvelope, error)
FetchPeerKey returns the verified registration record for addr (cached). The hash(identity)==address check defeats key substitution by the server.
func (*ChatClient) Identity ¶ added in v0.25.9
func (c *ChatClient) Identity() *ChatIdentity
Identity returns the client identity.
func (*ChatClient) IsRegistered ¶ added in v0.25.9
func (c *ChatClient) IsRegistered(ctx context.Context, peer [protocol.AddressSize]byte) (bool, error)
IsRegistered reports whether peer can be messaged here. (false, nil) = no record; a non-nil error is transient.
func (*ChatClient) NextSeq ¶ added in v0.25.9
func (c *ChatClient) NextSeq(ctx context.Context, peer [protocol.AddressSize]byte) (uint32, error)
NextSeq returns the next usable message sequence for peer, recovered from the server (client amnesia safe).
func (*ChatClient) PeerStatus ¶ added in v0.25.9
func (c *ChatClient) PeerStatus(ctx context.Context, peer [protocol.AddressSize]byte) (accepted, delivered uint32, err error)
PeerStatus returns (last_accepted ✓, last_delivered ✓✓) for own messages sent to peer. PeerStatus returns ✓/✓✓ counters, serialized with opSeq so it cannot swap the session out from under an in-progress upload.
func (*ChatClient) Quota ¶ added in v0.25.9
func (c *ChatClient) Quota() (remaining uint16, resetUnix uint32, known bool)
Quota returns the latest server-reported send quota.
func (*ChatClient) Register ¶ added in v0.25.9
func (c *ChatClient) Register(ctx context.Context, _ ChatProgress) error
Register establishes a session (registering the identity on first contact).
func (*ChatClient) Registered ¶ added in v0.25.9
func (c *ChatClient) Registered() bool
Registered reports whether this identity established a session (and thus registered) on the server during this process lifetime.
func (*ChatClient) SendMessage ¶ added in v0.25.9
func (c *ChatClient) SendMessage(ctx context.Context, peer [protocol.AddressSize]byte, seq uint32, text string, progress ChatProgress) (*ChatSendResult, error)
SendMessage encrypts and uploads one message. seq must be greater than the pair's last accepted seq (use NextSeq to recover it).
func (*ChatClient) ServerAdvertisesChat ¶ added in v0.25.9
func (c *ChatClient) ServerAdvertisesChat(ctx context.Context) (advertises, known bool)
ServerAdvertisesChat is the cheap availability pre-check: it reads the signed feed's ChatAvailable bit from metadata block 0 — one query, no ChatInfo retries. A chatless server has no ChatInfo channel, so the full probe only fails after several retried DNS fetches; this short-circuits that. known=false means block 0 didn't carry the flag or the fetch failed, so the caller should fall back to the authoritative EnsureInfo probe rather than assume anything.
func (*ChatClient) SetBudget ¶ added in v0.25.9
func (c *ChatClient) SetBudget(b int)
SetBudget sets the per-cell op-plaintext budget B (clamped to the valid range). Smaller B = shorter query names but more cells per message.
type ChatIdentity ¶ added in v0.25.9
type ChatIdentity struct {
Seed []byte
Identity ed25519.PrivateKey
Enc *ecdh.PrivateKey
Addr [protocol.AddressSize]byte
}
ChatIdentity holds the seed-derived chat keys.
func NewChatIdentity ¶ added in v0.25.9
func NewChatIdentity(seed []byte) (*ChatIdentity, error)
NewChatIdentity derives a chat identity from a seed.
type ChatIncoming ¶ added in v0.25.9
type ChatIncoming struct {
From [protocol.AddressSize]byte
Seq uint32
Text string
}
ChatIncoming is one decrypted inbox message.
type ChatProgress ¶ added in v0.25.9
type ChatProgress func(done, total int)
ChatProgress reports upload/download progress: done out of total units.
type ChatSendResult ¶ added in v0.25.9
ChatSendResult reports a committed send.
type ChatStatusError ¶ added in v0.25.9
type ChatStatusError struct {
Op byte
Status byte
Remaining uint16
ResetUnix uint32
LastAccepted uint32
}
ChatStatusError is a non-OK chat response status.
func (*ChatStatusError) Error ¶ added in v0.25.9
func (e *ChatStatusError) Error() string
type Fetcher ¶
type Fetcher struct {
// contains filtered or unexported fields
}
Fetcher fetches feed blocks over DNS.
func NewFetcher ¶
NewFetcher creates a new DNS block fetcher.
func (*Fetcher) AllResolvers ¶
AllResolvers returns all user-configured resolvers.
func (*Fetcher) ExportStats ¶ added in v0.9.0
ExportStats returns a snapshot of all resolver stats.
func (*Fetcher) FetchBlock ¶
FetchBlock fetches a single encrypted block from the given channel. It enqueues through the rate limiter and respects ctx cancellation. On transient failure it retries up to 20 times with a short back-off.
func (*Fetcher) FetchChannel ¶
func (f *Fetcher) FetchChannel(ctx context.Context, channelNum int, blockCount int) ([]protocol.Message, error)
FetchChannel fetches all blocks for a channel and returns the parsed messages. Cancelling ctx immediately aborts any queued or in-flight block fetches. Each block is retried individually via FetchBlock before the channel fetch fails.
func (*Fetcher) FetchChannelVerified ¶ added in v0.10.0
func (f *Fetcher) FetchChannelVerified(ctx context.Context, channelNum int, blockCount int, expectedHash uint32) ([]protocol.Message, error)
FetchChannelVerified works like FetchChannel but additionally verifies that the parsed messages match the expected content hash from metadata. Returns ErrContentHashMismatch when the hash does not match (block-version race).
func (*Fetcher) FetchLatestVersion ¶ added in v0.7.0
FetchLatestVersion fetches the latest release version from the dedicated version channel. The block is padded to a random size matching regular content blocks (DPI-resistant). Empty string means unknown/unavailable.
func (*Fetcher) FetchMedia ¶ added in v0.13.0
func (f *Fetcher) FetchMedia(ctx context.Context, channel uint16, blockCount uint16, expectedCRC32 uint32, progress MediaProgress) ([]byte, error)
FetchMedia returns the assembled bytes of a media blob served on a media channel, optionally verifying expectedCRC32.
func (*Fetcher) FetchMediaBlocksStream ¶ added in v0.13.0
func (f *Fetcher) FetchMediaBlocksStream(ctx context.Context, channel, startBlock, count uint16, w io.Writer, progress MediaProgress) error
FetchMediaBlocksStream fetches blocks [startBlock, startBlock+count) and writes each block's raw bytes to w in order as soon as they become contiguous. No header parsing; callers slice off the protocol header themselves and decompress as appropriate. Cancelling ctx aborts both in-flight DNS queries and pending writes.
func (*Fetcher) FetchMetadata ¶
FetchMetadata returns the current metadata. Block 0 of channel 0 may carry an extended header (magic + block_count + hash) packed into the otherwise-unused Marker + Timestamp fields. New servers emit that; old servers don't. We always fetch block 0 first; if it has the magic we use the fast parallel path, otherwise we fall back to the legacy "fetch-until-parse-succeeds" loop. The cooldown only kicks in when the fast path is repeatedly broken (e.g., snapshot churn that hash-verify can't ride out), in which case we go straight to legacy for a while.
func (*Fetcher) FetchProfilePicDirectory ¶ added in v0.16.0
func (f *Fetcher) FetchProfilePicDirectory(ctx context.Context) (ProfilePicsBundle, error)
FetchProfilePicDirectory pulls the bundle directory from ProfilePicsChannel — header (bundle metadata + relay availability) and per-username entries. The bundle bytes themselves are NOT fetched here; callers do that with FetchMedia(BundleChannel, BundleBlocks, BundleCRC) once and then slice locally.
Returns (zero-value bundle, nil) when the server has no profile pics configured (or is older and doesn't know the channel).
func (*Fetcher) FetchRelayInfo ¶ added in v0.13.0
FetchRelayInfo pulls the relay-info payload from RelayInfoChannel. Block 0 carries a uint16 total-block count prefix; if more than one block is needed the rest are fetched in parallel and concatenated. An empty payload yields a zero-value RelayInfo.
func (*Fetcher) FetchTitles ¶ added in v0.11.0
FetchTitles fetches and decodes the channel display name map from TitlesChannel. Returns an empty map (not an error) when the server does not support TitlesChannel. Block 0 carries a uint16 total-block count prefix; remaining blocks are fetched in parallel so the overall fetch is bounded by the slowest single block, not the sum.
func (*Fetcher) HasServerKey ¶ added in v0.25.9
HasServerKey reports whether a server signing key is pinned.
func (*Fetcher) ImportStats ¶ added in v0.9.0
ImportStats loads previously exported stats into this fetcher.
func (*Fetcher) QueryMode ¶ added in v0.25.9
func (f *Fetcher) QueryMode() protocol.QueryEncoding
QueryMode returns the configured query encoding (single base32 label or the feed's multi-label hex). Chat cells honor it so they blend with feed traffic.
func (*Fetcher) QueryTotals ¶ added in v0.25.9
QueryTotals returns cumulative (responses, errors) summed across all resolvers. Snapshotting the delta around an operation tells the caller how many queries it issued (responses+errors) and how many were lost (errors) — the same success/failure signal the resolver scoreboard is built on.
func (*Fetcher) RecordFailure ¶ added in v0.3.0
RecordFailure records a failed DNS query for the given resolver.
func (*Fetcher) RecordSuccess ¶ added in v0.3.0
RecordSuccess records a successful DNS query for the given resolver.
func (*Fetcher) RemoveActiveResolver ¶ added in v0.9.0
RemoveActiveResolver removes a resolver from the active pool.
func (*Fetcher) ResetStats ¶ added in v0.9.0
func (f *Fetcher) ResetStats()
ResetStats clears all resolver scoring data.
func (*Fetcher) ResolverScoreboard ¶ added in v0.9.0
func (f *Fetcher) ResolverScoreboard() []ResolverInfo
ResolverScoreboard returns stats for all active resolvers sorted by score descending.
func (*Fetcher) ScanConcurrency ¶ added in v0.5.0
ScanConcurrency returns how many resolvers the scanner should probe in parallel, derived from the configured rate limit. Rule: concurrency = max(1, floor(rateQPS)). If rateQPS is 0 (unlimited), falls back to the default of 10.
func (*Fetcher) SendAdminCommand ¶
func (f *Fetcher) SendAdminCommand(ctx context.Context, cmd protocol.AdminCmd, arg string) (string, error)
SendAdminCommand sends an admin command to the server via chunked upstream DNS queries. The payload is a single AdminCmd byte followed by the argument string.
func (*Fetcher) SendMessage ¶
SendMessage sends a text message to the given channel via chunked upstream DNS queries. Returns an error if the message is too long or sending fails.
func (*Fetcher) SetActiveResolvers ¶
SetActiveResolvers updates the healthy resolver pool. Called by ResolverChecker.
func (*Fetcher) SetCacheEpoch ¶ added in v0.19.0
SetCacheEpoch updates the deterministic seed. Pass the latest Metadata NextFetch (or any monotonically-advancing per-server value) after every successful metadata refresh; advancing it invalidates cached responses.
func (*Fetcher) SetCacheShare ¶ added in v0.19.0
SetCacheShare toggles the shared-resolver-cache feature. When on, queries to eligible channels use a deterministic suffix derived from the cache epoch so public resolvers can serve identical responses across users.
func (*Fetcher) SetDomains ¶ added in v0.25.9
SetDomains sets the extra sub-domains feed queries are spread across, in addition to the main domain from NewFetcher (which stays first/canonical). Blanks, the main domain, and duplicates are ignored. Call at init.
func (*Fetcher) SetLogFunc ¶
SetLogFunc sets the debug log callback.
func (*Fetcher) SetNoiseDisabled ¶ added in v0.25.9
SetNoiseDisabled suppresses the decoy-DNS noise generator for this fetcher. Used by the per-profile chat fetchers, which piggyback on the main feed fetcher's cover traffic — running one noise loop per profile would multiply background DNS for no benefit. Call before Start.
func (*Fetcher) SetQueryMode ¶
func (f *Fetcher) SetQueryMode(mode protocol.QueryEncoding)
SetQueryMode sets the DNS query encoding mode.
func (*Fetcher) SetRateLimit ¶
SetRateLimit sets the maximum queries per second (0 = unlimited). Must be called before Start.
func (*Fetcher) SetResolvers ¶
SetResolvers replaces the full resolver list and resets the active pool.
func (*Fetcher) SetScatter ¶ added in v0.3.0
SetScatter sets the number of resolvers queried simultaneously per DNS block request. 1 = sequential (no scatter). Values > 1 fan out to N resolvers and use the fastest response. Must be called before Start().
func (*Fetcher) SetServerPublicKey ¶ added in v0.25.9
SetServerPublicKey pins the server signing key (base64url, no padding, as printed by genserverkey / the sk= URI field). Empty disables verification.
func (*Fetcher) SetStatsForward ¶ added in v0.25.9
SetStatsForward registers a callback that receives a copy of every success/failure so a child fetcher can feed results back to the parent.
func (*Fetcher) SetTimeout ¶
SetTimeout sets the per-query DNS timeout.
func (*Fetcher) Start ¶
Start launches background goroutines (rate limiter and, unless disabled, the noise generator). ctx controls their lifetime — cancel it to cleanly stop them. Call once per fetcher configuration; creating a new fetcher replaces the old one.
func (*Fetcher) UpdateResolverPool ¶ added in v0.10.0
UpdateResolverPool replaces the full resolver list and removes any active resolvers that are no longer in the bank.
type Gap ¶ added in v0.5.1
type Gap struct {
AfterID uint32 `json:"after_id"`
BeforeID uint32 `json:"before_id"`
Count int `json:"count"`
}
Gap represents a range of missing messages detected between two consecutive cached messages (IDs gap > 1, capped at 500 to exclude natural Telegram gaps).
type MediaProgress ¶ added in v0.13.0
type MediaProgress func(completed, total int)
MediaProgress reports per-block progress (completed of total). May be invoked from a background goroutine.
type MessagesResult ¶ added in v0.5.1
type MessagesResult struct {
Messages []protocol.Message `json:"messages"`
Gaps []Gap `json:"gaps"`
}
MessagesResult is the wire type for /api/messages/<n>. It carries the full merged history plus any detected gaps.
func NewMessagesResult ¶ added in v0.5.1
func NewMessagesResult(msgs []protocol.Message) *MessagesResult
NewMessagesResult wraps a raw message slice with gap detection. Used as a fallback when the on-disk cache is unavailable.
type ProfilePicEntry ¶ added in v0.16.0
type ProfilePicEntry struct {
Username string
Offset uint32
Size uint32
CRC uint32
MIME uint8
DNSChannel uint16
DNSBlocks uint16
}
ProfilePicEntry points at one avatar in two ways:
GitHub bundle path: bytes are bundle[Offset:Offset+Size]; CRC must equal CRC32-IEEE of that slice (use protocol.VerifyEntry). Per-entry DNS path: bytes live on DNS channel DNSChannel with DNSBlocks blocks. CRC and Size are checked the same way.
The client picks whichever path is reachable. With the bundle path one HTTPS request fetches every avatar; with the DNS path each avatar is fetched independently so partial sets still show up.
func (ProfilePicEntry) Extension ¶ added in v0.16.0
func (p ProfilePicEntry) Extension() string
Extension returns ".jpg" / ".png" / ".webp" for caching on disk.
func (ProfilePicEntry) MimeString ¶ added in v0.16.0
func (p ProfilePicEntry) MimeString() string
MimeString returns "image/jpeg" / "image/png" / "image/webp" for the MIME tag, suitable for use as an HTTP Content-Type.
type ProfilePicsBundle ¶ added in v0.16.0
type ProfilePicsBundle struct {
BundleSize uint32
BundleCRC uint32
// Relays describes where the bundle is reachable, indexed by
// RelayDNS / RelayGitHub. RelayGitHub means the bundle is on
// GitHub. RelayDNS for the bundle is rarely true — the standard
// DNS path uses per-entry channels (see ProfilePicEntry).
Relays []bool
Entries []ProfilePicEntry
}
ProfilePicsBundle is the client-side view of the profile-pic directory. The bundle (Size/CRC/Relays) describes the GitHub-served concatenated blob; per-entry DNSChannel/DNSBlocks describe an independent DNS fallback for that single avatar.
func (ProfilePicsBundle) HasRelay ¶ added in v0.16.0
func (b ProfilePicsBundle) HasRelay(idx int) bool
HasRelay forwards to the relay availability bit at idx.
type RelayInfo ¶ added in v0.13.0
type RelayInfo struct {
GitHubRepo string // "owner/repo"
}
RelayInfo carries the relay-discovery data the server publishes on RelayInfoChannel. Empty fields mean "not configured".
func ParseRelayInfo ¶ added in v0.13.0
ParseRelayInfo decodes the relay-info payload (one "key=value" pair per line). Unknown keys are ignored so future relays can be added without breaking older clients.
type ResolverChecker ¶
type ResolverChecker struct {
// contains filtered or unexported fields
}
ResolverChecker periodically probes the fetcher's configured resolvers and updates the active (healthy) resolver pool. It replaces the old file/CIDR scanner — no file I/O; just a plain DNS probe on channel 0.
func NewResolverChecker ¶
func NewResolverChecker(fetcher *Fetcher, timeout time.Duration) *ResolverChecker
NewResolverChecker creates a health checker for the resolvers in fetcher. timeout is the per-probe deadline; 0 uses a 15-second default.
func (*ResolverChecker) CancelCurrentScan ¶ added in v0.4.0
func (rc *ResolverChecker) CancelCurrentScan()
CancelCurrentScan cancels any in-progress CheckNow call, causing it to return early without updating the resolver list.
func (*ResolverChecker) CheckNow ¶
func (rc *ResolverChecker) CheckNow(ctx context.Context) bool
CheckNow runs a single resolver health-check pass immediately. If a scan is already in progress the call is a no-op (returns false). Returns true if the scan ran to completion. Use CancelCurrentScan to abort a running scan from outside.
func (*ResolverChecker) SetAutoScan ¶ added in v0.10.0
func (rc *ResolverChecker) SetAutoScan(enabled bool)
SetAutoScan enables or disables the hourly periodic health-check loop.
func (*ResolverChecker) SetLogFunc ¶
func (rc *ResolverChecker) SetLogFunc(fn LogFunc)
SetLogFunc sets the callback used to emit health-check results to the log panel.
func (*ResolverChecker) SetOnScanDone ¶ added in v0.5.0
func (rc *ResolverChecker) SetOnScanDone(fn func([]string))
SetOnScanDone registers a callback invoked after each completed CheckNow pass with the list of healthy resolver addresses. Not called when the scan is cancelled.
func (*ResolverChecker) Start ¶
func (rc *ResolverChecker) Start(ctx context.Context)
Start begins the periodic health-check loop in the background. An initial check runs immediately; subsequent checks happen every 10 minutes. ctx controls the lifetime — cancel it to stop the checker.
func (*ResolverChecker) StartAndNotify ¶ added in v0.2.0
func (rc *ResolverChecker) StartAndNotify(ctx context.Context, onFirstDone func())
StartAndNotify is like Start but calls onFirstDone (if non-nil) after the first successful health-check pass (i.e. at least one resolver is healthy), before the periodic ticker begins. If the initial scan finds zero healthy resolvers it retries every minute until at least one resolver becomes reachable (or ctx is cancelled). Safe to call only once per checker instance; subsequent calls are no-ops.
func (*ResolverChecker) StartPeriodic ¶ added in v0.5.0
func (rc *ResolverChecker) StartPeriodic(ctx context.Context)
StartPeriodic starts only the periodic Hour health-check loop without running an initial scan. Use when resolvers are already available (e.g. loaded from a saved last-scan file on startup). Safe to call only once per checker instance; subsequent calls are no-ops.
type ResolverInfo ¶ added in v0.9.0
type ResolverInfo struct {
Addr string `json:"addr"`
Score float64 `json:"score"`
Success int64 `json:"success"`
Failure int64 `json:"failure"`
AvgMs float64 `json:"avgMs"`
}
ResolverInfo holds public stats for a single resolver.
type ResolverScanner ¶ added in v0.7.0
type ResolverScanner struct {
// contains filtered or unexported fields
}
ResolverScanner scans IP ranges to find working DNS resolvers for thefeed.
func NewResolverScanner ¶ added in v0.7.0
func NewResolverScanner() *ResolverScanner
NewResolverScanner creates a new scanner instance.
func (*ResolverScanner) Pause ¶ added in v0.7.0
func (rs *ResolverScanner) Pause()
Pause pauses the scanner.
func (*ResolverScanner) Progress ¶ added in v0.7.0
func (rs *ResolverScanner) Progress() ScannerProgress
Progress returns the current scan progress.
func (*ResolverScanner) Resume ¶ added in v0.7.0
func (rs *ResolverScanner) Resume()
Resume resumes the scanner from pause.
func (*ResolverScanner) SetDebug ¶ added in v0.7.0
func (rs *ResolverScanner) SetDebug(v bool)
SetDebug enables or disables verbose scanner logging.
func (*ResolverScanner) SetLogFunc ¶ added in v0.7.0
func (rs *ResolverScanner) SetLogFunc(fn LogFunc)
SetLogFunc sets the log callback.
func (*ResolverScanner) Start ¶ added in v0.7.0
func (rs *ResolverScanner) Start(cfg ScannerConfig) error
Start begins scanning with the given config. Returns error if already running.
func (*ResolverScanner) State ¶ added in v0.7.0
func (rs *ResolverScanner) State() ScannerState
State returns the current scanner state.
func (*ResolverScanner) Stop ¶ added in v0.7.0
func (rs *ResolverScanner) Stop()
Stop stops the scanner.
type ScannerConfig ¶ added in v0.7.0
type ScannerConfig struct {
// Targets is a list of IPs, domains, or CIDRs to scan.
Targets []string `json:"targets"`
// MaxIPs limits how many IPs to scan from the expanded list (0 = all).
MaxIPs int `json:"maxIPs"`
// RateLimit is the concurrent probe limit (default 50).
RateLimit int `json:"rateLimit"`
// Timeout is the per-probe timeout in seconds (default 10).
Timeout float64 `json:"timeout"`
// ExpandSubnet: if true, when a working resolver is found, also scan its /24.
ExpandSubnet bool `json:"expandSubnet"`
// QueryMode is "single" or "double".
QueryMode string `json:"queryMode"`
// Domain is the thefeed server domain.
Domain string `json:"domain"`
// Passphrase is the encryption key.
Passphrase string `json:"passphrase"`
}
ScannerConfig holds the configuration for a resolver scan.
type ScannerProgress ¶ added in v0.7.0
type ScannerProgress struct {
State ScannerState `json:"state"`
Total int `json:"total"`
Scanned int `json:"scanned"`
Found int `json:"found"`
Results []ScannerResult `json:"results"`
Error string `json:"error,omitempty"`
StartedAt int64 `json:"startedAt,omitempty"`
}
ScannerProgress holds the current progress of the scanner.
type ScannerResult ¶ added in v0.7.0
type ScannerResult struct {
IP string `json:"ip"`
LatencyMs float64 `json:"latencyMs"` // milliseconds
FoundAt int64 `json:"foundAt"` // unix timestamp
}
ScannerResult represents a single working resolver found by the scanner.
type ScannerState ¶ added in v0.7.0
type ScannerState string
ScannerState represents the current state of the scanner.
const ( ScannerIdle ScannerState = "idle" ScannerRunning ScannerState = "running" ScannerPaused ScannerState = "paused" ScannerDone ScannerState = "done" )