pairflow

package
v0.19.2 Latest Latest
Warning

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

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

Documentation

Overview

Package pairflow owns the device-pairing mint flow shared by the CLI (`gadak pairing`, GDK-433/450/797) and the desktop app's Devices tab (GDK-1047): endpoint resolution and validation, the scoped mint plus offer encode, the _home routing-token guard, list row shaping, and the two QR encodings (the terminal module matrix and the PNG a phone scans).

One owner so the surfaces cannot drift. The guard that motivates the extraction is ensureHomeRoutingToken: once one device token exists the serve gate takes Bearer only — no loopback bypass — so a mint that skips the home routing token locks the standalone home out of its own passthrough. The CLI carried that guard; before this package the desktop would have had to copy it, and a copy is where a guard goes missing.

Presentation stays with the callers: this package returns values (the offer, the loopback flag, which _home action ran) and prints nothing.

Index

Constants

View Source
const DefaultTTL = 90 * 24 * time.Hour

DefaultTTL is 90 days: a device token is revocable, so it does not need a short life, but an unattended one should not outlive a quarter by default either.

Variables

This section is empty.

Functions

func AdvertisedEndpoint

func AdvertisedEndpoint(cfg *config.Config) string

AdvertisedEndpoint turns a live UI-serve listen address into the URL form endpoint validation wants. Discovery is origin.LiveServeFor, the single owner of the serveaddr walk. An explicit endpoint is not normalized: making the user name the scheme is the point.

func DefaultTTLFlag

func DefaultTTLFlag() string

DefaultTTLFlag renders DefaultTTL in the <N><unit> syntax --ttl speaks, so the 90-day default has one owner.

func Dir

func Dir(cfg *config.Config) (string, error)

Dir is the profile directory the token store lives in. Pairing protects both gated surfaces of a home serve — the origin passthrough (standalone, GDK-433) and the mirror REST a phone companion reads (GDK-797) — so both workspace kinds may mint; what stays closed for a connected workspace is the passthrough itself (origin_rest.go 404s it). A workspace that is itself paired away cannot mint: its home is another machine.

func EndpointFromAdvertise

func EndpointFromAdvertise(addr string) string

EndpointFromAdvertise upgrades the raw bind address the advertise file stores (host:port) to the URL form, leaving an address that already carries a scheme alone.

func GateOpen

func GateOpen(dir string, now time.Time) bool

GateOpen reports whether the serve gate has fallen back open: no active token exists (including _home). The GDK-481 sentence callers print is theirs; this is the one boolean behind it.

func MintHome

func MintHome(dir string, cfg *config.Config, endpoint string, now time.Time) (pairing.Meta, error)

MintHome is `pairing mint --label _home`: routing-token rotation, not a device offer. No offer is produced. Standalone only — a connected workspace writes go straight to its site, and the routing file here would make origin.pairedRemote read this workspace as paired with its own serve.

func PairedLine

func PairedLine(cfg *config.Config, rem *pairing.Remote) string

PairedLine is the self-status sentence a paired workspace gets where it tried to act as a home.

func ParseTTL

func ParseTTL(s string) (time.Duration, error)

ParseTTL accepts one integer with a single unit — "90d", "24h", "30m", "45s". time.ParseDuration rejects "d", and inventing compound syntax ("1d12h") is surface nobody asked for; a clear error beats guessing.

func QRModules

func QRModules(offer string) ([][]bool, error)

QRModules encodes the offer at EC Medium and returns the module matrix, quiet zone included (the library default 4-module border). Medium rather than Lower: pairing happens once per device, and a code that scans from a phone held at an angle beats a dense one; higher than Medium buys almost nothing at this payload size.

func QRPNG

func QRPNG(offer string) ([]byte, error)

QRPNG renders the offer QR as a PNG: white background, black modules, quiet zone included via the module matrix, square. The desktop serves it to the webview as a data URI; the geometry matches what the terminal half-block renderer draws, so both encodings are the same code.

func Revoke

func Revoke(dir, selector string, now time.Time) (pairing.Meta, error)

Revoke is the store call the CLI and desktop share: selector is an exact label or a hash prefix of at least 8 hex characters (what Rows prints). The _home refusal and ambiguity wording live in internal/pairing — callers classify by that error, they do not rewrite it.

Types

type HomeRoutingAction

type HomeRoutingAction string

HomeRoutingAction is what a mint did about the _home routing token.

const (
	// HomeRoutingNone: the existing credential is valid, or the gate is
	// off, or the workspace is connected (no routing token by design).
	HomeRoutingNone HomeRoutingAction = ""
	// HomeRoutingMinted: this mint created the first _home credential.
	HomeRoutingMinted HomeRoutingAction = "minted"
	// HomeRoutingReissued: the stored credential was stale or revoked
	// while the gate was on (GDK-450 recovery).
	HomeRoutingReissued HomeRoutingAction = "reissued"
)

type MintResult

type MintResult struct {
	Offer, Label, Scope, Endpoint, ExpiresAt string
	Meta                                     pairing.Meta
	// LoopbackWarning says the endpoint is a loopback address — remote
	// devices cannot reach it. The caller renders its own copy.
	LoopbackWarning bool
	// HomeRouting names what ensureHomeRoutingToken did, so the CLI can
	// print its stderr note and the desktop can stay quiet.
	HomeRouting HomeRoutingAction
}

MintResult is everything a mint produced. The offer is a credential: it exists here and in the caller output, never in a log or an error.

func MintDevice

func MintDevice(dir string, cfg *config.Config, label, scope, ttl, endpoint string, now time.Time) (MintResult, error)

MintDevice is the device-mint flow: validate, resolve the endpoint, mint the scoped token, encode the offer, and — for a standalone home — keep the _home routing token valid. Callers pre-validate their own input surface (flags, form fields); this function re-validates because it is the structural owner, not a trusted callee.

endpoint may be empty: a live serve for cfg's profile is then discovered via origin.LiveServeFor. ttl may be empty: DefaultTTL applies. On a failure after the token was minted (the routing-token step is the only one) the result still carries the offer: the plaintext exists exactly once, in the caller output, or a minted token would be stranded unrecoverable.

type Row

type Row struct {
	Hash     string `json:"hash"`
	Label    string `json:"label"`
	Scope    string `json:"scope"`
	Created  string `json:"created"`
	Expires  string `json:"expires"`
	LastUsed string `json:"last_used"`
	State    string `json:"state"`
}

Row is one pairing-list row. JSON tags are the table columns — never the plaintext token, never the full hash (only the 8-char prefix the list already showed).

func RowFrom

func RowFrom(m pairing.Meta, now time.Time) Row

RowFrom shapes one stored token. The 8-char hash prefix is the same floor revoke accepts.

func Rows

func Rows(dir string, now time.Time) ([]Row, error)

Rows shapes every stored token for listing: the _home row reads as local-routing (not a device scope), and state distinguishes revoked and expired from active.

Jump to

Keyboard shortcuts

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