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 built-in 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
- func AdvertisedEndpoint(cfg *config.Config) string
- func DefaultTTLFlag() string
- func Dir(cfg *config.Config) (string, error)
- func EndpointFromAdvertise(addr string) string
- func GateOpen(dir string, now time.Time) bool
- func MintHome(dir string, cfg *config.Config, endpoint string, now time.Time) (pairing.Meta, error)
- func PairedLine(cfg *config.Config, rem *pairing.Remote) string
- func ParseScopeList(s string) ([]string, error)
- func ParseTTL(s string) (time.Duration, error)
- func QRModules(offer string) ([][]bool, error)
- func QRPNG(offer string) ([]byte, error)
- func Revoke(dir, selector string, now time.Time) ([]pairing.Meta, error)
- type HomeRoutingAction
- type LoopbackEndpointError
- type MintResult
- type Row
Constants ¶
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 ¶
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 ¶
Dir is the profile directory the token store lives in. Pairing protects both gated surfaces of a home serve — the origin passthrough (builtIn, 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 ¶
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 ¶
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 ¶
MintHome is `pairing mint --label _home`: routing-token rotation, not a device offer. No offer is produced. Built-in 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 ¶
PairedLine is the self-status sentence a paired workspace gets where it tried to act as a home.
func ParseScopeList ¶ added in v0.21.0
ParseScopeList parses --scope: one scope, or a comma list like "serve,terminal" (GDK-1498 — one offer, one token per scope). Members are trimmed, validated against the three real scopes, and returned in canonical order, so "terminal,serve" and "serve,terminal" mint the same offer. Duplicates are refused: the offer carries one token per scope, so a repeated name cannot mean two different tokens.
func ParseTTL ¶
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 ¶
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 ¶
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 ¶
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). A label selector revokes every live token carrying it — a device is one label (GDK-1498) — so the plural return; a hash prefix still selects exactly one. 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 LoopbackEndpointError ¶ added in v0.20.0
type LoopbackEndpointError struct{ Endpoint string }
LoopbackEndpointError is the GDK-1266 refusal: no --endpoint was given and the live serve's own address is loopback, so no remote device could use the offer. Callers add their own prescription (the CLI: a completed command); the endpoint is here so they can name it.
func (*LoopbackEndpointError) Error ¶ added in v0.20.0
func (e *LoopbackEndpointError) Error() string
type MintResult ¶
type MintResult struct {
Offer, Label, Scope, Endpoint, ExpiresAt string
Meta pairing.Meta
// Scopes is every scope the offer carries and Metas the matching
// store rows, in the offer's order (GDK-1498 multi-scope mint). A
// single-scope mint has one entry each and keeps using the scalar
// fields above — the readers that predate multi-scope keep working.
Scopes []string
Metas []pairing.Meta
// LoopbackWarning says the explicit endpoint is a loopback address —
// only a device on this machine can reach it. The caller renders its
// own copy. (A discovered loopback endpoint is refused instead:
// LoopbackEndpointError, GDK-1266.)
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 built-in 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.
func MintDeviceMulti ¶ added in v0.21.0
func MintDeviceMulti(dir string, cfg *config.Config, label string, scopes []string, ttl, endpoint string, now time.Time) (MintResult, error)
MintDeviceMulti is the multi-scope device-mint flow (GDK-1498): one offer, one token per scope, one device. A single-scope list delegates to MintDevice so a single-scope offer stays the byte-identical v1 line every existing pairer was promised (GDK-799). scopes is re-validated here — canonicalized and duplicate-free — because this function is the structural owner, not a trusted callee.
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).