store

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Jun 3, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package store defines the ReceiveStore boundary and its skeleton implementation: the clipboard channel (disk save / last_path / recents) and the agent channel (in-memory FIFO inbox + long-poll waiters). The long-poll primitives are the per-waiter buffered chan + sync.Mutex model (PLAN §3.10), the Go analog of Swift ReceiveStore's per-waiter boxed continuations.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ReadLastPath added in v0.1.1

func ReadLastPath(file string) (string, bool)

ReadLastPath reads the persisted last_path file (the bare absolute path, no trailing newline) and returns (path, true) when present, else ("", false). It is the daemonless on-disk fallback for `clipbeam last`/`wait` when no daemon is reachable (fix [E]): FinishClipboard already persists last_path on disk (concrete.go), so the read verbs can recover it without the in-memory daemon. An empty/missing/unreadable file is ("",false).

Types

type ReceiveStore

type ReceiveStore interface {
	// SaveClipboardItem streams one clipboard-channel image/file item's payload from
	// r to the save dir (sanitize → temp 0600 → fsync → atomic rename →
	// collision-suffix), via io.CopyBuffer (32 KB) so a 50 MB item is never held
	// whole in RAM (PLAN §3.7). It returns the absolute saved path and the number of
	// decoded bytes written (so Ingest can do the per-item write→add→check, §3.8).
	// last_path/recents updates are deferred to FinishClipboard.
	SaveClipboardItem(name, kind, uti string, r io.Reader) (path string, written int64, err error)

	// SaveClipboardText handles a clipboard-channel text item. It writes a .txt
	// sidecar (and returns its path) only when saveTextToDisk OR utf8 >
	// longTextThreshold; otherwise it returns ("", false, 0, nil) — the short-text
	// carve-out that is NOT counted toward maxBytes (PLAN §3.8, §7.2). written is the
	// sidecar byte count (0 when no sidecar is written).
	SaveClipboardText(text string) (path string, wroteSidecar bool, written int64, err error)

	// FinishClipboard commits last_path (no trailing newline) to the most-recent
	// saved path and appends savedPaths to recents (last 20, 0600), then resumes any
	// parked WaitForNext waiter (PLAN §7.2). Called once after all items are saved.
	FinishClipboard(sender string, savedPaths []string) error

	// SaveAgentItem streams one agent-channel image/file item from r to the agent
	// inbox (dir 0700, files 0600) and enqueues an AgentItem carrying its path.
	// Returns the absolute saved path and the decoded byte count (PLAN §7.5). The
	// payload file is also recorded in the durable on-disk journal ONLY when the store
	// was built with JournalAgentItems=true (the daemonless `clipbeam ingest` verb,
	// fix [F]); the serve daemon builds the store WITHOUT that flag, so it stays
	// in-memory-FIFO-only with no journal write (no pileup, no double-delivery, H1/H2).
	SaveAgentItem(sender, name, kind, uti string, r io.Reader) (path string, written int64, err error)

	// EnqueueAgentText enqueues an agent-channel text item in the in-memory FIFO. It is
	// ALSO written to the durable on-disk journal ONLY when the store was built with
	// JournalAgentItems=true (the daemonless `clipbeam ingest` verb, fix [F]); the serve
	// daemon builds the store WITHOUT that flag, so text stays in memory only — never
	// written to disk (PLAN §7.5; no plaintext retention, no double-delivery, H1/H2).
	EnqueueAgentText(sender, text string) error

	// LastPath returns the most-recently-saved clipboard path, or ("", false) if
	// none. Powers GET /last (PLAN §7.7).
	LastPath() (string, bool)

	// Recv dequeues exactly one agent FIFO item, or parks until ctx is done
	// (timeout → nil item). Powers GET /recv (PLAN §3.10).
	Recv(ctx context.Context) (*wire.AgentItem, error)

	// DrainAgentDisk atomically claims and returns the OLDEST durable agent-channel
	// journal record (FIFO), or a nil item when the journal is empty. It is a ONE-SHOT
	// disk drain (NOT a long-poll) used by the daemonless `clipbeam recv` fallback when
	// no daemon is reachable (fix [F]); it is exactly-once across concurrent drainers.
	DrainAgentDisk(ctx context.Context) (*wire.AgentItem, error)

	// WaitForNext parks until the next clipboard path is saved or ctx is done
	// (latest-wins, not a queue). Powers the fixed-120 s GET /wait (PLAN §3.10).
	WaitForNext(ctx context.Context) (string, error)
}

ReceiveStore is the receive-side state boundary, implemented by the concrete store (M1b) and consumed by the httpd handlers (M1c). It is the Go analog of the Swift ClipReceiver protocol (Server.swift:204) split into its two channels.

The clipboard channel writes disk + last_path + recents (PLAN §7.2); the agent channel enqueues to an in-memory FIFO drained one-at-a-time by Recv (PLAN §7.5). Neither method sets a system clipboard or notifies in v1 (PLAN §7.4).

func New

func New(cfg StoreConfig) (ReceiveStore, error)

New constructs the concrete ReceiveStore over the given save dir, agent inbox dir, last_path file, recents file, and the text-sidecar policy. The in-memory agent FIFO + clipboard waiter primitives (PLAN §3.10) and the bounded-memory disk-save pipeline (sanitize → temp 0600 → fsync → atomic rename → collision, §3.7) are fully wired. Any persisted last_path is loaded so GET /last survives a restart.

type StoreConfig

type StoreConfig struct {
	SaveDir           string
	AgentInboxDir     string
	LastPathFile      string
	RecentsFile       string
	SaveTextToDisk    bool
	LongTextThreshold int
	MaxBytes          int

	// JournalAgentItems opts the store into the durable on-disk agent journal (fix [F]).
	// It is set TRUE only by the one-shot, daemonless `clipbeam ingest` verb (a
	// short-lived process whose in-memory FIFO is discarded on exit, so a later
	// daemonless `clipbeam recv` — a DIFFERENT process — drains the journal instead).
	// It is FALSE for the serve daemon and any HTTP /clip,/agent-send handler path: the
	// daemon is the live consumer of its own in-memory FIFO via /recv, so it must NOT
	// also write the journal (that would grow unbounded with retained plaintext, H1, and
	// double-deliver to a later daemonless recv, H2). Default FALSE keeps the daemon
	// path byte-for-byte its pre-changeset in-memory-only behavior.
	JournalAgentItems bool
}

StoreConfig parameterizes the concrete store with the per-OS paths and the text-counting policy (PLAN §3.8, §4).

Jump to

Keyboard shortcuts

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