share

package
v0.4.2 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2026 License: GPL-3.0 Imports: 12 Imported by: 0

Documentation

Overview

Package share implements the daemon side of "share to phone": an on-demand, time-boxed LAN listener that serves the console's READ surface to a phone on the same network, guarded by a single short-lived read-only token.

Deliberately a THIRD listener, distinct from the daemon's loopback-bound standing ones: it binds the machine's LAN IPv4 on an ephemeral port and exists only while a share is active. There is no loopback guard - the phone is remote, which is the point - so the TOKEN is the sole gate, and listener and token are created and destroyed together so neither outlives the other.

This package is the subject of two figures on the docs site; edit them alongside it. magus:diagram daemon-share - the boxes "/api/v1/share" and "LAN listener". magus:diagram daemon-http - the box "/api/v1/share".

The console app is served from the SAME origin as its API on this listener, so the phone's browser never issues a cross-origin request and CORS never engages.

Index

Constants

View Source
const (
	MinTTL = 1 * time.Minute
	MaxTTL = 90 * 24 * time.Hour
)

MinTTL and MaxTTL bound a caller-requested share lifetime. The console lets the operator pick a duration before minting (a quick phone glance versus an all-day display on a TV), and the daemon clamps the request to this range so a leaked QR can never be made to live indefinitely. The token stays read-only regardless, so a longer window only widens who may look, never what they may change.

MaxTTL is 90 days because a wall display outlives any session and a link expiring nightly turns an ambient dashboard into a daily chore.

It stays BOUNDED, and short of a year. This is a bearer credential - whoever holds the URL is the audience - and URLs end up pasted into chat, photographed off a screen, left in a kiosk browser's history. An unexpiring share makes each of those permanent.

Variables

This section is empty.

Functions

func SelectLANIPv4

func SelectLANIPv4() (netip.Addr, error)

SelectLANIPv4 returns the machine's first up, non-loopback, private-range IPv4, or an error naming the shortfall. It gathers the real interfaces and delegates the choice to pickLANIPv4.

func WithTrailDir

func WithTrailDir(dir string) option

WithTrailDir points the manager at the activity-trail base directory so that the first request from each remote device on a live share records a "share link opened" event. Empty disables recording (the trail is never a precondition for serving a share). It replaces the old SetTrailDir setter, so the wiring is set once at construction and there is no "call it before Start" ordering trap.

Types

type Manager

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

Manager owns the at-most-one active share. Start opens a fresh listener (superseding any current one); Close tears the current one down. It is safe for concurrent use.

func NewManager

func NewManager(parent context.Context, ttl time.Duration, log *slog.Logger, opts ...option) *Manager

NewManager returns a Manager whose shares live for ttl (<=0 uses defaultTTL) and whose listeners are torn down when parent is cancelled (daemon shutdown).

func (*Manager) Active

func (m *Manager) Active() (TokenInfo, bool)

Active returns secret-free metadata for the currently live share token, or ok=false when no share is active. A share whose token has already expired (in the brief window before its context fires and clears m.cur) reports ok=false, so a management surface never lists a dead token as if it were revocable.

func (*Manager) Close

func (m *Manager) Close()

Close tears down the active share, if any. Idempotent. Called on daemon shutdown so no listener outlives the process.

func (*Manager) CloseIf

func (m *Manager) CloseIf(fingerprint string) bool

CloseIf tears the active share down only when its token fingerprint (the first 8 hex of the SHA-256, as Manager.Active reports it) still equals fingerprint, and reports whether it did. It is the atomic check-and-close a revoke needs: a caller that read the active fingerprint via Active and then called Close could, in the window between the two, race a supersede and tear down a DIFFERENT share minted in the meantime. CloseIf re-checks identity while holding the lock, so it revokes exactly the share the caller named or nothing - a lost race leaves the new share alive and returns false (the revoke maps that to NotFound).

func (*Manager) Start

func (m *Manager) Start(consoleDir string, guarded map[string]http.Handler, ttl time.Duration) (Session, error)

Start mints a fresh read-only token and opens a new LAN listener serving the console from consoleDir at /console/ (unauthenticated static assets) and every handler in guarded behind the new token (path -> handler). Any previously active share is revoked first, so there is exactly one live token bound 1:1 to exactly one live listener: a token from a prior session validates nowhere. The listener closes and the token expires together after ttl (or on parent cancellation / Close). ttl is the caller-requested lifetime: a non-positive value uses the manager's configured default, and any other value is clamped to [MinTTL, MaxTTL]. consoleDir must contain the built console. No ctx parameter on purpose: a share OUTLIVES the request that opened it, so accepting the caller's context invites the wrong wiring - the HTTP handler passes r.Context(), which would tear the share down the instant that POST returned.

type Session

type Session struct {
	// URL is the full link (with the token in the fragment) a phone loads.
	URL string
	// ExpiresAt is when the listener closes and the token dies.
	ExpiresAt time.Time
	// Superseded reports whether starting this share revoked a previous active
	// one, so the UI can tell the user the old QR just died.
	Superseded bool
}

Session is the public description of an active share, returned to the console.

type TokenInfo

type TokenInfo struct {
	Fingerprint string
	Scope       string
	Created     time.Time
	Expires     time.Time
}

TokenInfo is the secret-free description of the active share token, for a management surface (the console Settings token list). Fingerprint is the prefix-only identifier used to revoke it; it never contains the token bytes.

Jump to

Keyboard shortcuts

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