Documentation
¶
Overview ¶
Package xfer pkg/skychat/xfer/manager.go c4-app-chat
Package xfer pkg/skychat/xfer/xfer.go c4-app-chat
xfer is skychat's file-transfer primitive: it moves one file over a single skywire stream (a dmsg stream or a skynet route — the caller supplies the DialFunc/listener, exactly like pkg/skychat/call). Files are conversation messages, Telegram-style: the Offer is announced into chat/group history and the bytes stream out-of-band on skyenv.SkychatFilePort so a large transfer never blocks the chat-message conn.
Wire protocol on one transfer stream (all frames are pkg/skychat/message length-prefixed frames; the DATA between them is raw):
- Offer frame (sender → receiver: id, name, size, mime, from, group)
- Accept frame (receiver → sender: ok, err) — the receiver's AcceptFunc decides here, BEFORE any bytes move, so a declining or over-quota peer never receives the body
- exactly offer.Size raw bytes (only when Accept.ok)
- Trailer frame (sender → receiver: sha256 of the streamed bytes)
- Receipt frame (receiver → sender: id, ok, err) — final verify verdict
The digest is a trailer, not part of the offer, so the sender streams in a single pass (works for non-seekable sources such as stdin); the receiver hashes what it reads and compares against the trailer before accepting the file as complete. All bytes ride the encrypted skywire transport — nothing touches the clearnet.
Index ¶
Constants ¶
const ( // IdleWindow bounds how long a transfer may make NO progress before it is // abandoned. It is deliberately an idle timeout and not a total one: a // total deadline is a file-size limit in disguise (a 35 MB file over a // slow route legitimately takes minutes), whereas a dead peer still fails // within one window. IdleWindow = 60 * time.Second )
const MaxOfferSize = message.MaxFrameSize
MaxOfferSize caps an Offer/Trailer/Receipt control frame. The DATA is not bounded by this — only offer.Size bounds it, and the AcceptFunc is free to reject an over-large Size before a single byte is read.
Variables ¶
var ( // ErrNoReceipt reports that the body AND its digest trailer were written in // full but the receiver's verdict never came back. The bytes are on the // peer's side — only the confirmation is missing — so a caller should treat // this as delivered-but-unconfirmed rather than as a failed transfer. ErrNoReceipt = errors.New("xfer: no receipt") )
Functions ¶
func IsDeclined ¶
IsDeclined reports whether err is the sentinel returned by Receive when the AcceptFunc declined the transfer (so callers can treat it as non-fatal).
func IsNoReceipt ¶
IsNoReceipt reports whether err means "fully delivered, verdict unknown" — the body and its digest went out in full but the receiver's receipt never came back. Callers should treat this as sent, not failed.
Types ¶
type AcceptFunc ¶
AcceptFunc decides an inbound transfer. It receives the peer that opened the stream and the Offer, and returns the destination to stream into plus whether to accept. The app implements "auto-accept when already paired 1:1 or sharing a group" here, and points dst at a file under the downloads dir. Returning ok false declines the transfer (the sender gets a Receipt with OK=false).
type Config ¶
type Config struct {
// LocalPK is this node's public key (stamped into outgoing offers).
LocalPK cipher.PubKey
// Dial opens a transfer stream to a peer (dmsg or skynet, same port).
Dial DialFunc
// Port is the file-transfer port (skyenv.SkychatFilePort) — the port this
// manager listens on and dials out to.
Port uint16
// Accept decides inbound transfers (auto-accept paired/group peers). Required
// to receive; if nil, every inbound transfer is declined.
Accept AcceptFunc
// OnDone, if set, is called after each transfer completes (either direction)
// with the final error (nil on success) — the app uses it to drop a message
// into chat/group history and update progress.
OnDone func(dir Direction, peer cipher.PubKey, offer Offer, err error)
// Logger is optional.
Logger *logging.Logger
}
Config configures a Manager.
type DialFunc ¶
DialFunc opens a transfer stream to peer:port over a skywire network (a dmsg stream or a skynet route). Same shape and contract as voice.DialFunc — the manager supplies one that tries whichever network is up, always on the SAME port (skyenv.SkychatFilePort) across dmsg and skynet.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager accepts inbound file transfers on one port over every skywire network it's given a listener for (dmsg + skynet, same port — like voice), and dials out to send. It is safe for concurrent use.
func NewManager ¶
NewManager builds a Manager. A nil Accept means inbound transfers are declined.
func (*Manager) AddListener ¶
AddListener starts accepting on one more listener after Serve is already running (e.g. the skynet listener that comes up only once the skynet networker registers). It returns immediately; accepting runs until ctx is done.
func (*Manager) SendFile ¶
func (m *Manager) SendFile(ctx context.Context, peer cipher.PubKey, offer Offer, r io.Reader) (Receipt, error)
SendFile dials peer and streams the file described by offer from r. It stamps offer.From with the local PK. The returned Receipt carries the receiver's verdict; a non-nil error is a transport/protocol failure before a verdict.
type Offer ¶
type Offer struct {
ID string `json:"id"` // unique per transfer
Name string `json:"name"` // suggested basename (no path)
Size int64 `json:"size"` // byte count
MIME string `json:"mime,omitempty"` // best-effort content type
From cipher.PubKey `json:"from"` // sender PK
Group string `json:"group,omitempty"` // group id when shared to a group (else 1:1)
}
Offer describes a file being sent. It is the first frame on a transfer stream and doubles as the metadata rendered as a message in the conversation.
func Receive ¶
func Receive(ctx context.Context, from cipher.PubKey, conn net.Conn, accept AcceptFunc) (Offer, error)
Receive reads an Offer from conn, asks accept where to put it (and whether to take it at all), streams offer.Size bytes into the destination while hashing, verifies the digest against the sender's trailer, and returns a Receipt to the sender. It returns the Offer and a nil error on a verified, accepted transfer; errDeclined when accept says no; a non-nil error on any wire/verify failure (the receiver still tries to send a negative Receipt so the sender learns). conn is NOT closed here.
type Receipt ¶
Receipt is the receiver's verdict, returned to the sender at the end.