common

package
v0.4.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 15, 2026 License: MPL-2.0 Imports: 44 Imported by: 0

Documentation

Overview

SPDX-License-Identifier: MPL-2.0 Copyright (c) 2025 KeibiSoft S.R.L. This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.

SPDX-License-Identifier: MPL-2.0 Copyright (c) 2025 KeibiSoft S.R.L. This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.

SPDX-License-Identifier: MPL-2.0 Copyright (c) 2025 KeibiSoft S.R.L. This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.

Index

Constants

View Source
const (
	ModeDirectOut = "direct-out"
	ModeDirectIn  = "direct-in"
)

Connection modes of the mixed shape. direct-out is the joiner's view: its outbound leg (its reads) is direct and its inbound comes from the bridge. direct-in is the creator's: it accepted the direct leg and dialed the bridge for its outbound.

View Source
const (
	StateIdle           = "idle"
	StateWaitingForPeer = "waiting_for_peer"
	StateConnected      = "connected"
	StateReconnecting   = "reconnecting"
	StateGaveUp         = "gave_up"
	StateMountGone      = "mount_gone"
	StateMountFailed    = "mount_failed"
)

Session states, as scripts see them.

View Source
const InviteBase = "https://keibidrop.com/join.html#"

InviteBase is the page an invite link points at. The code goes in the fragment, so it never reaches the web server's log. The page is join.html; /join is not a route.

View Source
const InviteSchemeBase = "kd://join/"

InviteSchemeBase opens an installed app instead of the page. No release registers the scheme with the OS yet.

View Source
const InviteWebBase = "https://web.keibidrop.com/#"

InviteWebBase is the browser peer. It reads the same fragment.

View Source
const RelaySlowNotice = "Reads are slow because the relay lane is shared right now. A data pack gives you the fast lane: " + TokensBuyURL

RelaySlowNotice is said once per session on the free relay lane, the first time a reader waits seconds for one fetch. The state line carries the short form ("shared right now") for as long as the session lasts.

View Source
const Timeout = 10*60 - 5

Timeout is the peer-join wait budget in seconds: 10 minutes minus a 5s margin, matching the relay registration TTL.

View Source
const TokenUnitBytes int64 = 10 << 20

TokenUnitBytes is the bandwidth one chain step buys. PROTOCOL CONSTANT shared with the relay ledger, the bridge and the token service.

View Source
const TokensBuyURL = "https://tokens.keibidrop.com/buy" //nolint:gosec // G101: URL, not a credential

TokensBuyURL is where money changes hands. Prices live on that page and in the token service's pack table, never in this binary.

Variables

View Source
var (
	Version    = "0.4.7" // Last release. Build ldflags overwrite both values from the tag.
	CommitHash = "dev"
)
View Source
var (
	ErrNilPointer                    = errors.New("nil pointer")
	ErrEmptyFingerprint              = errors.New("fingerprint is empty")
	ErrInvalidLength                 = errors.New("invalid length")
	ErrRelayAtMaximumCapacity        = errors.New("relay at maximum capacity")
	ErrRateLimitHit                  = errors.New("relay rate limit hit, retry in 5 minutes")
	ErrMissingFingerprint            = errors.New("missing fingerprint header")
	ErrInvalidPayload                = errors.New("invalid registration payload")
	ErrMissingKeys                   = errors.New("missing public keys")
	ErrInvalidFingerprint            = errors.New("invalid fingerprint format")
	ErrServerError                   = errors.New("server error")
	ErrTemporaryRetry                = errors.New("temporary network issue")
	ErrTimeoutReached                = errors.New("timeout reached")
	ErrConnectCancelled              = errors.New("connect cancelled")
	ErrFingerprintMismatch           = errors.New("fingerprint mismatch")
	ErrRelayAtFullCapacityRetryLater = errors.New("relay at full capacity, retry later")
	ErrNotFound                      = errors.New("not found")
	ErrInvalidResponse               = errors.New("invalid response")
	ErrInvalidIP                     = errors.New("invalid IP")
	ErrSessionNotEstablished         = errors.New("session not established")
	ErrFilesystemAlreadyMounted      = errors.New("filesystem already mounted")
	ErrNilFilesystem                 = errors.New("filesystem not mounted")
	ErrAlreadyRunning                = errors.New("already running")
	ErrInvalidSession                = errors.New("invalid session")
	ErrServerAtCapacity              = errors.New("relay server at capacity, please try again in 5 minutes")
	ErrIdenticalFingerprints         = errors.New("own and peer fingerprints are identical")
	ErrDownloadPaused                = errors.New("download paused")
	ErrListenerNotOpen               = errors.New("can't accept a peer connection right now, please try again")
)
View Source
var ErrNoQUICForFold = errors.New("no quic channel for fold")

ErrNoQUICForFold routes the eager fold to TCP when no QUIC channel is up. It never escapes runEagerFold.

Functions

func DecideLocalRole added in v0.3.5

func DecideLocalRole(myName, peerName, peerAddr string) bool

DecideLocalRole reports whether this peer should create (listen) rather than join (dial) for a local-mode connection to peerName at peerAddr. It feeds LocalConnectRole this peer's LAN IPv4 and the peer's bare IP (port/zone stripped), so colliding names compare like-for-like.

func DialQUICControl added in v0.4.0

func DialQUICControl(ctx context.Context, s *session.Session, peerUDPAddr string) (net.Conn, *transport.MigratableConn, error)

DialQUICControl brings up the outbound QUIC control channel to peerUDPAddr. It wraps a migratable QUIC stream in the outbound SecureConn and returns the migration handle. It errors when the peer negotiated no QUIC key, so the caller falls back to TCP-only.

func GetDiscoveryLANAddress added in v0.3.5

func GetDiscoveryLANAddress() string

GetDiscoveryLANAddress returns this machine's private IPv4 in the form peers see via discovery. The local-connect tiebreak needs the same form on both sides. It returns "" when no private IPv4 exists.

func GetGlobalIPv6

func GetGlobalIPv6() (string, error)

GetGlobalIPv6 returns a stable, non-temporary global IPv6 address. RFC 4941 privacy extensions rotate temporary addresses. Connections bound to a temporary address break when the OS deprecates it.

func GetJSONWithURL

func GetJSONWithURL(client *http.Client, endpoint *url.URL, headers map[string]string, mapError ErrorMapperFunc) (*http.Response, error)

func GetLinkLocalAddress

func GetLinkLocalAddress(port int) (string, error)

GetLinkLocalAddress returns a link-local IPv6 address as "ip%zone:port" for direct LAN peer connections. It falls back to loopback when no link-local interface exists.

func GetLocalAddrs

func GetLocalAddrs() []string

GetLocalAddrs returns all private and link-local IP addresses for LAN discovery. The relay registration includes them, so same-network peers can connect directly.

func GetLocalIPv6

func GetLocalIPv6() (string, error)
func InviteLink(code, base string) string

InviteLink wraps a peer code in a page address. base is empty for InviteBase, or an origin of your own. This is the only place a link is built and NormalizePeerCode is the inverse.

func ListenQUICControl added in v0.4.0

func ListenQUICControl(s *session.Session, udpAddr string) (net.Listener, error)

ListenQUICControl starts the inbound QUIC control listener on udpAddr. It wraps each accepted stream in the inbound SecureConn and errors when the peer negotiated no QUIC key.

func LocalConnectRole added in v0.3.5

func LocalConnectRole(myName, peerName, myAddr, peerAddr string) (create bool)

LocalConnectRole picks who creates (listens) vs joins (dials) in local mode. Smaller name creates; if names collide (random), smaller address breaks it.

func MountIsLive added in v0.4.7

func MountIsLive(path string) bool

MountIsLive reports whether path is a mount point, by comparing its device id with its parent's. A directory that merely exists is not enough: the mount point is created before the filesystem is attached, so stat alone would report ready while reads still fail.

func NewSingleConnListener

func NewSingleConnListener(conn net.Conn) net.Listener

func NormalizePeerCode added in v0.4.8

func NormalizePeerCode(s string) string

NormalizePeerCode takes a peer code as it arrives from a chat message: the bare code, or the invite link that carries it in a fragment (https://keibidrop.com/join#<code>) or in the path. A string that is neither comes back trimmed, so ValidateFingerprint reports the real error. The code alphabet is base64 RawURL, which has no "#" and no "/".

func ParsePeerDirectAddress

func ParsePeerDirectAddress(addr string) (ip string, zone string, port int, err error)

ParsePeerDirectAddress parses a direct LAN peer address in any stored form: "ip", "ip:port", "ip%zone", "ip%zone:port", "[ip]:port", or "[ip%zone]:port". It returns the bare IP, the zone ("" when absent), and the port (0 when absent). The IP must be link-local, loopback, or private. IPv6 link-local requires a zone.

func PostJSONWithURL

func PostJSONWithURL(client *http.Client, endpoint *url.URL, headers map[string]string, payload interface{}, mapError ErrorMapperFunc) (*http.Response, error)

func PrintBanner

func PrintBanner()

func RegisterErrorMapper

func RegisterErrorMapper(statusCode int, err error) error

func SanitizeLogContent

func SanitizeLogContent(raw string) string

SanitizeLogContent sanitizes log text in memory.

func SanitizeLogs

func SanitizeLogs(logPath string) (string, error)

SanitizeLogs reads a log file and returns sanitized content. It redacts file names (extensions stay), fingerprints, and IP addresses. It keeps timestamps, log levels, method names, error types, sizes, and connection events.

func SanitizeLogsToFile

func SanitizeLogsToFile(logPath, destPath string) error

SanitizeLogsToFile reads a log, sanitizes it, and writes to destPath.

func ValidateFingerprint

func ValidateFingerprint(fp string) error

Types

type BridgeStatus added in v0.4.1

type BridgeStatus struct {
	Addr       string  `json:"addr,omitempty"` // bridge host:port this session dials
	Via        string  `json:"via,omitempty"`  // display form: bridge hostname
	Paid       bool    `json:"paid"`           // bridge acked paid priority for this session
	Contention bool    `json:"contention"`     // bridge signaled contention in its ack
	Busy       bool    `json:"busy"`           // relay flagged the assigned bridge busy
	Notice     string  `json:"notice,omitempty"`
	WalletGB   float64 `json:"wallet_gb"`  // prepaid value left across chains
	SessionGB  float64 `json:"session_gb"` // bytes this session moved via the bridge, both directions
}

BridgeStatus is the relay-visibility surface: which bridge a session rides, whether it holds paid priority, and what the relay says about load. Shown by the CLI status, the kd daemon and the desktop UI.

type ConnectionHint

type ConnectionHint struct {
	IP    string `json:"ip"`             // Public IP address, v4 or v6.
	Port  int    `json:"port"`           // Where the peer listens.
	IPv6  bool   `json:"ipv6"`           // True when this hint prefers IPv6.
	Proto string `json:"proto"`          // For example "tcp".
	Note  string `json:"note,omitempty"` // Optional: NAT behavior notes.
	// InboundBlocked means nothing reaches the address above, so a dial only burns the
	// timeout. It rides inside the encrypted blob, so the relay never sees it. Older
	// peers omit it, which decodes as false.
	InboundBlocked bool `json:"inbound_blocked,omitempty"`
}

type CreditStatus added in v0.4.1

type CreditStatus struct {
	WalletGB float64 `json:"wallet_gb"`
	Level    string  `json:"level"` // "ok", "low", "critical", "empty"
	Notice   string  `json:"notice,omitempty"`
	BuyURL   string  `json:"buy_url"`
}

CreditStatus is the level-readable relay credit state. Events are edges; agents and frontends poll this for the current level.

type EnableOpts added in v0.2.0

type EnableOpts struct {
	PassphraseProtect  bool
	PassphraseProvider func() (string, error)
	ExternalMaster     []byte // 32-byte key from mobile bridge (iOS Keychain / Android Keystore)
}

EnableOpts controls how persistent identity loads:

  • PassphraseProtect: opt into Tier 2, a passphrase-derived key. Requires a non-nil PassphraseProvider.
  • PassphraseProvider: runs once when PassphraseProtect is true to obtain the passphrase. Typical sources: TTY prompt, stdin bridge, test stub.

type EncryptedRegistration

type EncryptedRegistration struct {
	Blob   string `json:"blob"`             // base64-encoded ChaCha20-Poly1305 ciphertext
	Bridge string `json:"bridge,omitempty"` // relay-suggested bridge address (e.g., "fra1.bridge.keibisoft.com:26600")
	Tier   string `json:"tier,omitempty"`   // bandwidth tier: "free", "priority" (relay metadata, not encrypted)
	Busy   bool   `json:"busy,omitempty"`   // assigned bridge is in contention right now
	Notice string `json:"notice,omitempty"` // server-controlled text shown to the user with busy
}

EncryptedRegistration is the relay-visible payload, an opaque blob. Only peers with the shared room password can decrypt it.

type ErrorMapperFunc

type ErrorMapperFunc func(statusCode int, err error) error

ErrorMapperFunc maps server status errors to semantic errors.

type ImplFileStreamProvider

type ImplFileStreamProvider struct {
	// contains filtered or unexported fields
}

func NewImplStreamProviderDual added in v0.4.0

func NewImplStreamProviderDual(bulk, fast bindings.KeibiServiceClient) *ImplFileStreamProvider

NewImplStreamProviderDual routes bulk prefetch and the read-ahead window to TCP, the reads a blocked reader waits on and chunk hashes to QUIC. A cache miss then does not queue behind a running prefetch or behind the window.

func (*ImplFileStreamProvider) GetChunkHashes added in v0.3.6

func (sp *ImplFileStreamProvider) GetChunkHashes(ctx context.Context, path string, chunkSize, fromChunk, count uint64) (types.ChunkHashReceiver, error)

GetChunkHashes requests per-chunk xxh3-64 fingerprints from the peer. An older peer returns codes.Unimplemented. The caller decides the fallback.

func (*ImplFileStreamProvider) OpenRemoteFile

func (sp *ImplFileStreamProvider) OpenRemoteFile(ctx context.Context, inode uint64, path string) (types.RemoteFileStream, error)

func (*ImplFileStreamProvider) OpenRemoteFileBulk added in v0.4.7

func (sp *ImplFileStreamProvider) OpenRemoteFileBulk(ctx context.Context, inode uint64, path string) (types.RemoteFileStream, error)

OpenRemoteFileBulk opens a read stream for predicted fetches: TCP first, QUIC when TCP is dead. The read-ahead window fetches on it (types.BulkReadOpener), so the miss lane carries only the reads a blocked reader waits on.

func (*ImplFileStreamProvider) ReadBatch added in v0.4.2

func (sp *ImplFileStreamProvider) ReadBatch(ctx context.Context, paths []string, maxBytes uint64) (types.BatchFrameReceiver, error)

ReadBatch starts a batched small-file download via the server-streaming ReadBatch RPC. It uses preferBulk routing so warming never queues ahead of latency-sensitive on-demand reads. An older peer surfaces ErrBatchUnsupported at the first Recv, not at open.

func (*ImplFileStreamProvider) StreamFile

func (sp *ImplFileStreamProvider) StreamFile(ctx context.Context, path string, startOffset uint64) (types.StreamFileReceiver, error)

StreamFile starts a push-based download via the server-streaming StreamFile RPC. It uses preferBulk routing: TCP first, QUIC when TCP is dead.

func (*ImplFileStreamProvider) WithFetchHook added in v0.4.7

func (sp *ImplFileStreamProvider) WithFetchHook(h func(active bool)) *ImplFileStreamProvider

WithFetchHook installs the fetch-in-flight callback and returns the provider.

type ImplRemoteFileStream

type ImplRemoteFileStream struct {
	// contains filtered or unexported fields
}

func (*ImplRemoteFileStream) Close

func (rfs *ImplRemoteFileStream) Close() error

func (*ImplRemoteFileStream) ReadAt

func (rfs *ImplRemoteFileStream) ReadAt(ctx context.Context, offset int64, size int64) ([]byte, error)
type InviteLinks struct {
	Code string `json:"code"`
	// Page is the one to send a person. It carries the download too.
	Page string `json:"invite_link"`
	Web  string `json:"web_link"`
	App  string `json:"app_link"`
}

InviteLinks is one peer code as the addresses that carry it. Every surface answers with these field names.

func InviteLinksFor added in v0.4.8

func InviteLinksFor(code string) InviteLinks

InviteLinksFor builds every form of one code.

type KeibiDrop

type KeibiDrop struct {
	RelayEndoint *url.URL

	Identity    *identity.DeviceIdentity
	AddressBook *identity.AddressBook
	Incognito   bool

	IsFUSE         bool
	IsLocalMode    bool
	BridgeAddr     string // TCP bridge relay address for firewall traversal
	StrictMode     bool   // Disables the data relay fallback: direct connections only.
	ConnectionMode string // "lan", "direct", or "bridge". Set after a successful connection.
	OpInProgress   atomic.Int32

	PeerIPv6IP     string
	PeerIPv4IP     string   // The peer's public IPv4 from its registration; "" when it has none.
	PeerLocalAddrs []string // LAN IPs from the relay registration, for same-network direct connect.

	LocalIPv6IP string

	// Filesystem.
	FS       *filesystem.FS
	KDSvc    *service.KeibidropServiceImpl
	KDClient bindings.KeibiServiceClient

	// Non-FUSE fallback.
	SyncTracker *synctracker.SyncTracker

	// Paths for the virtual mount point and the save folder.
	ToMount string
	ToSave  string

	// Collab sync options.
	PrefetchOnOpen bool
	PushOnWrite    bool
	// AutoCache enables the macFUSE auto_cache mount option, so a peer's same-size
	// in-place edit shows live. The caller sets it from config.LiveCollab. False = git-safe.
	AutoCache           bool
	PrefetchAutoMB      int    // Files >= this many MB auto-prefetch on open. From config.PrefetchAutoMB. 0 = off.
	ReadAheadWindowMB   int    // Cap in MB for predictive sequential read-ahead. From config.ReadAheadWindowMB. 0 = off.
	ScanSharedOnStart   bool   // Announce files already in ToSave when a session starts. From config.ScanSharedOnStart.
	RescanSharedSeconds int    // Re-walk ToSave this often while a session runs and announce new files. From config.RescanSharedSeconds. 0 = off.
	AutoConnectPeer     string // Saved contact to connect to on startup, with retry. From config.AutoConnectPeer.
	ShareReadOnly       bool   // Refuse every inbound peer mutation at the service layer. From config.ShareReadOnly.
	MountReadOnly       bool   // Local FUSE mount returns EROFS on write ops. From config.MountReadOnly.
	PreserveMetadata    bool   // Apply the origin's mode and times to files saved on disk. From config.PreserveMetadata.

	Cancel context.CancelFunc // Exported so the FFI layer can call it for app exit.

	// Event callback. The FFI layer wires it to push events to the UI.
	OnEvent func(string)

	// OnPeerVerified fires with the peer's verified fingerprint when the handshake
	// confirms identity, before any files sync. setupFilesystem wires it to scope the
	// FUSE cache to the peer: drop another peer's view, keep the same peer's.
	OnPeerVerified func(fp string)

	// Connection resilience.
	HealthMonitor    *session.HealthMonitor
	ReconnectManager *session.ReconnectManager
	RelayKeepalive   *RelayKeepalive
	// contains filtered or unexported fields
}

func NewKeibiDrop

func NewKeibiDrop(ctx context.Context, logger *slog.Logger, isFuse bool, relayURL *url.URL, inboundPort int, defaultOutboundPort int, toMount string, toSave string, prefetchOnOpen bool, pushOnWrite bool) (*KeibiDrop, error)

NewKeibiDrop builds a KeibiDrop and probes the local global IPv6 address.

func NewKeibiDropWithIP

func NewKeibiDropWithIP(ctx context.Context, logger *slog.Logger, isFuse bool, relayURL *url.URL, inboundPort int, defaultOutboundPort int, toMount string, toSave string, prefetchOnOpen bool, pushOnWrite bool, ipv6Address string) (*KeibiDrop, error)

NewKeibiDropWithIP is NewKeibiDrop with an explicit IPv6 address instead of a network probe. It enables tests on machines without a global IPv6 address.

func (*KeibiDrop) AddFile

func (kd *KeibiDrop) AddFile(path string) error

Add a file to be tracked.

func (*KeibiDrop) AddFileAs

func (kd *KeibiDrop) AddFileAs(localPath string, remoteName string) error

AddFileAs adds a file with a custom remote name (preserving folder structure). Automatically sends ADD_DIR for any parent directories the peer may not have.

func (*KeibiDrop) AddPeerFingerprint

func (kd *KeibiDrop) AddPeerFingerprint(fp string) error

func (*KeibiDrop) AutoConnectArmed added in v0.4.7

func (kd *KeibiDrop) AutoConnectArmed() bool

AutoConnectArmed reports whether StartAutoConnect runs its watchdog.

func (*KeibiDrop) BackfillRemoteFilesIntoFS added in v0.4.1

func (kd *KeibiDrop) BackfillRemoteFilesIntoFS()

BackfillRemoteFilesIntoFS adds tracker-known remote files to the FUSE tree. ADD_FILE notifies that arrive before the first SetFS land only in SyncTracker.RemoteFiles; without this they never appear in the mount. Run calls it right after it publishes the mounted FS. Files already in the tree are left alone.

func (*KeibiDrop) BridgeInfo added in v0.4.1

func (kd *KeibiDrop) BridgeInfo() BridgeStatus

func (*KeibiDrop) CancelDownload

func (kd *KeibiDrop) CancelDownload(remoteName string) error

CancelDownload cancels an active download. The partial file and bitmap are preserved on disk so the next PullFile call resumes automatically.

func (*KeibiDrop) CancelPendingConnect added in v0.4.5

func (kd *KeibiDrop) CancelPendingConnect()

waitForPeerFingerprint blocks until the expected peer fingerprint is set (via AddPeerFingerprint or a relay lookup), polling once a second up to the connection Timeout. Returns ErrTimeoutReached if it never arrives. CancelPendingConnect makes a CreateRoom or JoinRoom still waiting for the peer return ErrConnectCancelled. Disconnect calls it, so the daemon does not stay busy for Timeout after the operator gave up.

func (*KeibiDrop) CheckContactPresence added in v0.2.0

func (kd *KeibiDrop) CheckContactPresence(fingerprint string) bool

CheckContactPresence reports whether the relay saw the contact online recently.

func (*KeibiDrop) Connect

func (kd *KeibiDrop) Connect() error

Connect determines the creator/joiner role automatically using deterministic fingerprint comparison and calls CreateRoom or JoinRoom. Lower fingerprint = creator (registers to relay, accepts inbound). Higher fingerprint = joiner (fetches from relay, dials out).

func (*KeibiDrop) ConnectToContact added in v0.2.0

func (kd *KeibiDrop) ConnectToContact(fingerprint string) error

ConnectToContact looks up a contact by fingerprint, registers it, and connects.

func (*KeibiDrop) ConnectionStatus

func (kd *KeibiDrop) ConnectionStatus() string

ConnectionStatus returns the current connection health status. It snapshots the monitor under kd.mu, because teardown nils the field under the same lock.

func (*KeibiDrop) CreateRoom

func (kd *KeibiDrop) CreateRoom() error

func (*KeibiDrop) DiskLow added in v0.4.7

func (kd *KeibiDrop) DiskLow() bool

DiskLow reports the save folder's disk under the floor at the last fetch.

func (*KeibiDrop) DowngradeListenerIPv6Only

func (kd *KeibiDrop) DowngradeListenerIPv6Only() error

DowngradeListenerIPv6Only replaces the dual-stack listener with an IPv6-only listener when local mode ends.

func (*KeibiDrop) EnablePersistentIdentity added in v0.2.0

func (kd *KeibiDrop) EnablePersistentIdentity(configDir string, opts EnableOpts) error

EnablePersistentIdentity replaces ephemeral keys with a stable device identity. Call it before CreateRoom/JoinRoom. It loads or creates the identity in configDir, rebuilds the session with stable keys, and loads the address book.

func (*KeibiDrop) EnablePersistentIdentityDefault added in v0.2.0

func (kd *KeibiDrop) EnablePersistentIdentityDefault(configDir string) error

EnablePersistentIdentityDefault calls EnablePersistentIdentity with zero-value EnableOpts: keychain or file tier, no passphrase.

func (*KeibiDrop) ExportFingerprint

func (kd *KeibiDrop) ExportFingerprint() (string, error)

func (*KeibiDrop) GetDownloadProgress

func (kd *KeibiDrop) GetDownloadProgress(remoteName string) float64

GetDownloadProgress returns the download progress for a file as a fraction [0.0, 1.0]. Returns -1 if the file has no active or resumable download.

func (*KeibiDrop) GetPeerFingerprint

func (kd *KeibiDrop) GetPeerFingerprint() (string, error)

func (*KeibiDrop) InboundBlocked added in v0.4.0

func (kd *KeibiDrop) InboundBlocked() bool

InboundBlocked reports whether to advertise the listener as unreachable. The mark binds to the address that observed it. A new address is a new network and re-probes, so one bad network cannot pin the node to the relay.

func (*KeibiDrop) InboundPort

func (kd *KeibiDrop) InboundPort() int

InboundPort returns the port this instance listens on for incoming connections.

func (*KeibiDrop) InitConnectionResilience

func (kd *KeibiDrop) InitConnectionResilience() error

func (*KeibiDrop) IsPeerPersistent added in v0.2.0

func (kd *KeibiDrop) IsPeerPersistent() bool

IsPeerPersistent returns whether the currently connected peer has a stable identity.

func (*KeibiDrop) IsRunning

func (kd *KeibiDrop) IsRunning() bool

IsRunning returns whether the KeibiDrop instance is in a connected session.

func (*KeibiDrop) JoinRoom

func (kd *KeibiDrop) JoinRoom() error

func (*KeibiDrop) ListFiles

func (kd *KeibiDrop) ListFiles() (remote []string, local []string)

func (*KeibiDrop) MigrateQUICControl added in v0.4.0

func (kd *KeibiDrop) MigrateQUICControl(ctx context.Context) error

MigrateQUICControl moves the live QUIC control connection to a fresh local UDP socket without a drop: QUIC keys the connection on connection ID, not the 5-tuple. It pings after, so the peer migrates its send path. On error the channel demotes and the maintainer re-establishes.

func (*KeibiDrop) MountFilesystem

func (kd *KeibiDrop) MountFilesystem(toMount string, toSave string, isSecond bool) error

This is blocking.

func (*KeibiDrop) NotifyDisconnect

func (kd *KeibiDrop) NotifyDisconnect()

NotifyDisconnect sends a best-effort DISCONNECT notification to the peer so they can clean up immediately instead of waiting for health monitor timeout.

func (*KeibiDrop) PeerInboundBlocked added in v0.4.0

func (kd *KeibiDrop) PeerInboundBlocked() bool

PeerInboundBlocked reports whether the peer advertised its listener as unreachable.

func (*KeibiDrop) PeerMixedLegs added in v0.4.7

func (kd *KeibiDrop) PeerMixedLegs() bool

PeerMixedLegs reports that the creator's registration says it keeps a direct leg when the joiner's inbound is blocked. A creator without it gets the old behaviour: both legs on the bridge.

func (*KeibiDrop) PeerRefusedWrites added in v0.4.2

func (kd *KeibiDrop) PeerRefusedWrites() uint64

PeerRefusedWrites returns how many local changes the peer refused because its share is read-only. Non-zero means the mount shows bytes the peer does not hold.

func (*KeibiDrop) PingQUICControl added in v0.4.0

func (kd *KeibiDrop) PingQUICControl(ctx context.Context) error

PingQUICControl sends one control Ping over the QUIC channel, or errors if it is down.

func (*KeibiDrop) ProbeInboundReachability added in v0.4.0

func (kd *KeibiDrop) ProbeInboundReachability(ctx context.Context)

ProbeInboundReachability asks the relay to dial the local listener and caches the verdict per local address for probeCacheTTL. Any failure leaves the mark unchanged.

func (*KeibiDrop) PublicIPv4 added in v0.4.7

func (kd *KeibiDrop) PublicIPv4() string

PublicIPv4 is the address the relay saw this host on, or "" before a probe answered over IPv4.

func (*KeibiDrop) PullFile

func (kd *KeibiDrop) PullFile(remoteName, localPath string) error

func (*KeibiDrop) PullFileWithParams

func (kd *KeibiDrop) PullFileWithParams(remoteName, localPath string, blockSize, nWorkers int) error

PullFileWithParams downloads remoteName to localPath using the specified blockSize (bytes per gRPC chunk) and nWorkers (parallel streams). Intended for benchmarking; production code uses PullFile with defaults.

func (*KeibiDrop) QUICControlConnected added in v0.4.0

func (kd *KeibiDrop) QUICControlConnected() bool

QUICControlConnected reports whether the outbound QUIC control channel is up and VERIFIED: the client is published only after a control Ping the peer answered.

func (*KeibiDrop) QUICInboundAccepted added in v0.4.1

func (kd *KeibiDrop) QUICInboundAccepted() uint64

QUICInboundAccepted reports how many inbound QUIC control conns this peer accepted. The outbound half can be up while this stays 0, which is a one-directional lane.

func (*KeibiDrop) QUICKeibiClient added in v0.4.0

func (kd *KeibiDrop) QUICKeibiClient() bindings.KeibiServiceClient

QUICKeibiClient returns a KeibiService client over the QUIC control channel, or nil when it is down. The channel is transport-isolated from TCP bulk, so metadata never queues behind a prefetch.

func (*KeibiDrop) QUICLaneStatus added in v0.4.1

func (kd *KeibiDrop) QUICLaneStatus() string

QUICLaneStatus reports the outbound lane as "up", or "down (<reason>)". kd status and peer-info show it, so a silently dead lane is visible without reading DEBUG logs.

func (*KeibiDrop) QUICMetadataSent added in v0.4.0

func (kd *KeibiDrop) QUICMetadataSent() uint64

QUICMetadataSent reports how many metadata RPCs rode the QUIC channel.

func (*KeibiDrop) QUICWriterEpoch added in v0.4.0

func (kd *KeibiDrop) QUICWriterEpoch() uint16

QUICWriterEpoch returns the highest writer key-epoch across the live QUIC control conns, or 0 when the channel is down. The QUIC lane ratchets independently of the TCP pair, so rekey observability must read both lanes.

func (*KeibiDrop) ReconnectionAttempts

func (kd *KeibiDrop) ReconnectionAttempts() int

ReconnectionAttempts returns the number of reconnection attempts.

func (*KeibiDrop) ReconnectionState

func (kd *KeibiDrop) ReconnectionState() string

ReconnectionState returns the current reconnection state. It snapshots the manager under kd.mu, because teardown nils the field under the same lock.

func (*KeibiDrop) ResolveContact added in v0.4.1

func (kd *KeibiDrop) ResolveContact(nameOrFingerprint string) (string, error)

ResolveContact maps a saved contact name (case-insensitive) or fingerprint to the contact's fingerprint.

func (*KeibiDrop) RevealHealth added in v0.4.1

func (kd *KeibiDrop) RevealHealth() map[string]any

RevealHealth reports the payment-reveal state for kd status: accepted count, consecutive failures, and the last error. Empty when the session is not funded.

func (*KeibiDrop) Run

func (kd *KeibiDrop) Run()

Run is the main loop. Call it as a goroutine.

func (*KeibiDrop) SaveCurrentPeerAsContact added in v0.2.0

func (kd *KeibiDrop) SaveCurrentPeerAsContact(name string) error

SaveCurrentPeerAsContact saves the currently connected peer as a named contact.

func (*KeibiDrop) ScanAndShareSaveDir added in v0.4.1

func (kd *KeibiDrop) ScanAndShareSaveDir(ctx context.Context) (int, error)

ScanAndShareSaveDir walks the save folder breadth-first and announces every regular file that is not yet tracked, over the same announce path AddFile uses: upsert LocalFiles, send ADD_FILE (in capped batches), persist to the shared store. Breadth-first, so the peer sees the top of the tree before deep subtrees. Nested files keep their save-root-relative path. Idempotent: tracked files are skipped, so restore, watcher events, and repeated scans compose. Returns the number of files announced. A send error retries briefly; on final failure the walk stops and the failed batch is untracked again, so the reconnect re-scan or the next session announces the rest.

func (*KeibiDrop) SessionState added in v0.4.7

func (kd *KeibiDrop) SessionState() SessionState

SessionState derives the state from what already exists: the run flag, the health monitor, the reconnect manager, the connect wait and the mount. It never blocks on the network.

func (*KeibiDrop) SetPeerDirectAddress

func (kd *KeibiDrop) SetPeerDirectAddress(addr string) error

SetPeerDirectAddress parses a direct LAN peer address (e.g. "fe80::1%eth0:26431"), stores the peer IP and port, and sets TOFU mode for the handshake.

func (*KeibiDrop) Shutdown

func (kd *KeibiDrop) Shutdown()

Shutdown permanently stops the Run goroutine. Use it for app exit. For a temporary disconnect, use Stop. Safe to call many times from any goroutine.

func (*KeibiDrop) Start

func (kd *KeibiDrop) Start()

Start signals the Run loop to begin a session.

func (*KeibiDrop) StartAutoConnect added in v0.4.1

func (kd *KeibiDrop) StartAutoConnect(ctx context.Context) error

StartAutoConnect resolves AutoConnectPeer and arms the connect watchdog. A no-op when the field is empty. Returns an error when the value does not resolve to a saved contact, so frontends can surface a clear message at startup instead of a silent dead loop.

func (*KeibiDrop) StartPresenceHeartbeat added in v0.2.0

func (kd *KeibiDrop) StartPresenceHeartbeat(ctx context.Context)

StartPresenceHeartbeat sends periodic presence heartbeats for all contacts. It runs until the caller cancels ctx. Call it after EnablePersistentIdentity.

func (*KeibiDrop) StartQUICControlChannel added in v0.4.0

func (kd *KeibiDrop) StartQUICControlChannel()

StartQUICControlChannel brings up the QUIC control pair alongside the TCP session: an inbound listener on the local UDP port and a background outbound dial to the peer. Any failure logs and leaves the session TCP-only. The dial never delays connect.

func (*KeibiDrop) StartThroughputSampler added in v0.4.7

func (kd *KeibiDrop) StartThroughputSampler(ctx context.Context)

StartThroughputSampler samples the wire counters once a second until ctx ends, so SessionState reports the rate over the last second. The daemons call it once at start; tests and short-lived callers need not.

func (*KeibiDrop) Stop

func (kd *KeibiDrop) Stop()

Stop cleanly disconnects the current session. Run() continues after cleanup, ready for the next CreateRoom/JoinRoom. Thread-safe.

func (*KeibiDrop) StopConnectionResilience

func (kd *KeibiDrop) StopConnectionResilience()

StopConnectionResilience stops all resilience components. It snapshots the handles under kd.mu, because teardown nils the fields under the same lock and external callers run concurrently with it. Stop calls run outside the lock.

func (*KeibiDrop) StopQUICControlChannel added in v0.4.0

func (kd *KeibiDrop) StopQUICControlChannel()

StopQUICControlChannel tears down the QUIC control pair. It is safe when the pair never came up. The listener close also retires a waiting serveQUICControl goroutine.

func (*KeibiDrop) ToggleIncognito added in v0.2.0

func (kd *KeibiDrop) ToggleIncognito(incognito bool, configDir string) (string, error)

ToggleIncognito switches between persistent and ephemeral identity. Enabling generates fresh ephemeral keys. Disabling restores the persistent keys. It returns the new fingerprint.

func (*KeibiDrop) TokensAdd added in v0.4.1

func (kd *KeibiDrop) TokensAdd(code string) (float64, error)

TokensAdd pastes a code into the wallet and returns its spendable size in GB. The ledger is asked once so a spent or never-minted code is refused at paste time instead of at first spend. No ledger answer keeps the add: the wallet is a cache and pasting offline must keep working.

func (*KeibiDrop) TokensBuyStart added in v0.4.1

func (kd *KeibiDrop) TokensBuyStart() string

TokensBuyStart returns the buy page URL to open in a browser, carrying a fresh claim ref, and starts a background poll that adds the purchased code to the wallet the moment the payment lands. No copy-paste, no account: the claim is a random one-shot ID, generated here, tied to nothing. Starting a new purchase replaces the previous poll.

func (*KeibiDrop) TokensCreditStatus added in v0.4.1

func (kd *KeibiDrop) TokensCreditStatus() CreditStatus

TokensCreditStatus reports remaining relay credit against the same thresholds noteCreditLevel uses. The copy stays honest: dry credit means the free tier on bridged transfers, slower only under contention, never a cutoff, and direct connections are never affected.

func (*KeibiDrop) TokensRefreshBalances added in v0.4.1

func (kd *KeibiDrop) TokensRefreshBalances() []TokenChainSummary

TokensRefreshBalances asks the relay ledger for each chain's remaining units and adopts its view (it is authoritative; the local count only lags).

func (*KeibiDrop) TokensSummaries added in v0.4.1

func (kd *KeibiDrop) TokensSummaries() []TokenChainSummary

TokensSummaries lists wallet chains for display.

func (*KeibiDrop) UnmountFilesystem

func (kd *KeibiDrop) UnmountFilesystem() error

func (*KeibiDrop) UnshareFile added in v0.3.0

func (kd *KeibiDrop) UnshareFile(name string) error

UnshareFile removes a file from the shared list and notifies the peer. Does NOT delete the file from disk.

func (*KeibiDrop) UpgradeListenerDualStack

func (kd *KeibiDrop) UpgradeListenerDualStack() error

UpgradeListenerDualStack replaces the IPv6-only listener with a dual-stack one. Local mode uses it: LAN discovery needs IPv4 connectivity.

func (*KeibiDrop) Wallet added in v0.4.1

func (kd *KeibiDrop) Wallet() *TokenWallet

Wallet lazily opens the machine-wide token wallet. Identity-independent by design: prepaid chains are bearer instruments, usable in incognito too.

func (*KeibiDrop) WireStats added in v0.4.1

func (kd *KeibiDrop) WireStats() (bytesSent, bytesRecv uint64)

WireStats sums bytes over the CURRENT conns: the session's two TCP conns plus the live QUIC control lanes. A reconnect, re-handshake, or lane change replaces conns and their counters vanish with them, so this is bytes since the current connections were installed, not a session total. WireStatsTotal is the figure that survives replacement.

func (*KeibiDrop) WireStatsTotal added in v0.4.2

func (kd *KeibiDrop) WireStatsTotal() (bytesSent, bytesRecv uint64)

WireStatsTotal returns bytes moved this daemon run: retired sessions and demoted lanes plus everything the live conns carry. Unlike WireStats it survives reconnects, re-handshakes, and QUIC lane changes. Known gap: an accepted-side QUIC conn replaced by a newer accept is not rolled up.

func (*KeibiDrop) WriterEpoch added in v0.4.0

func (kd *KeibiDrop) WriterEpoch() uint16

WriterEpoch reports the in-band ratchet generation: the max writer epoch across both live directions, or 0 before a monitor exists. It snapshots the monitor under kd.mu, because teardown nils the field under the same lock; the snapshot's captured conns are immutable, so the epoch read itself is race-free.

type PeerRegistration

type PeerRegistration struct {
	Fingerprint string            `json:"fingerprint"`
	PublicKeys  map[string]string `json:"public_keys"` // base64 encoded
	Listen      *ConnectionHint   `json:"listen"`
	// Listen4 is the same listener at the public IPv4 the relay saw this peer on,
	// with the relay's verdict on it. Omitted before a probe answered over IPv4;
	// older peers omit it always.
	Listen4    *ConnectionHint `json:"listen4,omitempty"`
	Reverse    *ConnectionHint `json:"reverse,omitempty"`
	LocalAddrs []string        `json:"local_addrs,omitempty"` // LAN IPs (192.168.x.x, fe80::x) for same-network detection
	Timestamp  int64           `json:"timestamp"`
	// MixedLegs is a capability: this peer keeps a direct leg a joiner opened when the
	// joiner's inbound is blocked, and bridges only its own outbound. A joiner reads
	// it before deciding to keep its leg; older peers omit it, which decodes as false.
	MixedLegs bool `json:"mixed_legs,omitempty"`
}

type RelayKeepalive

type RelayKeepalive struct {

	// Configuration
	Interval time.Duration // Refresh interval. Must stay under relayEntryTTL.
	// contains filtered or unexported fields
}

RelayKeepalive refreshes the relay registration before the TTL expires, so peers can always find each other.

func NewRelayKeepalive

func NewRelayKeepalive(kd *KeibiDrop, logger *slog.Logger) *RelayKeepalive

NewRelayKeepalive creates a new relay keepalive manager.

func (*RelayKeepalive) CheckIPChange

func (rk *RelayKeepalive) CheckIPChange() error

CheckIPChange refreshes the relay registration when the local IP changes. Call it periodically or on network state change.

func (*RelayKeepalive) FailureCount

func (rk *RelayKeepalive) FailureCount() int

FailureCount returns the number of consecutive refresh failures.

func (*RelayKeepalive) ForceRefresh

func (rk *RelayKeepalive) ForceRefresh() error

ForceRefresh refreshes the relay registration immediately. Call it on IP change or reconnection.

func (*RelayKeepalive) LastRefresh

func (rk *RelayKeepalive) LastRefresh() time.Time

LastRefresh returns the timestamp of the last successful refresh.

func (*RelayKeepalive) Pause

func (rk *RelayKeepalive) Pause()

Pause disables refresh, for example while disconnected.

func (*RelayKeepalive) Resume

func (rk *RelayKeepalive) Resume()

Resume re-enables refresh.

func (*RelayKeepalive) Start

func (rk *RelayKeepalive) Start()

Start begins the background refresh loop.

func (*RelayKeepalive) Stop

func (rk *RelayKeepalive) Stop()

Stop halts the background refresh loop.

type SessionState added in v0.4.7

type SessionState struct {
	// State is one of idle, waiting_for_peer, connected, reconnecting,
	// gave_up, mount_gone, mount_failed.
	State string `json:"state"`
	Text  string `json:"state_text"`
	// Mode is lan, direct, bridge, direct-out (our reads direct, the return leg on
	// the relay) or direct-in (the peer's reads direct); empty before a session.
	Mode string `json:"mode"`
	// Paid reports a funded relay lane. False on a free lane and off the bridge.
	Paid bool `json:"paid"`
	// Throttled reports a free relay lane that held a reader for seconds this
	// session. A data pack lifts it. Never true on a paid or direct session.
	Throttled bool `json:"throttled"`
	// DiskLow reports the save folder's disk under the free-space floor at the
	// last fetch. Reads that need bytes from the peer fail until space is freed.
	DiskLow bool `json:"disk_low"`
	// MountReady reports the FUSE folder mounted and served. False with FUSE off.
	MountReady  bool `json:"mount_ready"`
	Attempt     int  `json:"attempt"`
	MaxAttempts int  `json:"max_attempts"`
	// Bytes per second over the last sample interval; see StartThroughputSampler.
	RecvBps uint64 `json:"recv_bps"`
	SentBps uint64 `json:"sent_bps"`
}

SessionState is the one line every surface shows about the link. kd status, the kdmcp kd_status tool and the desktop app read the same struct, so a person at a terminal, an agent and a person at the window get the same words. State is stable for scripts; Text is for people.

type TaskSignal

type TaskSignal int
const (
	Start TaskSignal = iota
	Stop
)

type TokenChainSummary added in v0.4.1

type TokenChainSummary struct {
	Code      string  `json:"code"`
	GBTotal   float64 `json:"gb_total"`
	GBLeft    float64 `json:"gb_left"`
	UnitsLeft int     `json:"units_left"`
	Dead      bool    `json:"dead"`
	AddedAt   int64   `json:"added_at"`
}

TokenChainSummary is what CLIs and UIs display.

type TokenWallet added in v0.4.1

type TokenWallet struct {
	// contains filtered or unexported fields
}

TokenWallet is the on-disk stash of prepaid chains. Plaintext JSON at 0600: the seeds ARE cash by design, and the user was warned to back up the codes.

func (*TokenWallet) Add added in v0.4.1

func (w *TokenWallet) Add(code string) (*walletChain, error)

Add validates a pasted code and stores its chain. Duplicate anchors merge.

func (*TokenWallet) Summaries added in v0.4.1

func (w *TokenWallet) Summaries() []TokenChainSummary

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL