Help us shape the future of Go! Take the 2026 Go Developer Survey

discovery

package
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package discovery is how an app finds the running Studio with no configuration (plan B3): `weft studio` and `weft dev` write a small file, studio.json, naming the Studio they serve, and weft/otel and weft/runtime read it when nothing else names one.

The file is {"url","token","db","pid","started","version"}, mode 0600, written after the listener is bound (so the port in it is the real one) and removed on a clean exit. It lives in the first of:

  • ./.weft/, when that directory exists (the local sink's own directory, so a project's Studio is found by that project's apps — after a default `weft studio` it always exists, so run the app from the directory you ran `weft studio` in, or set WEFT_STUDIO_URL);
  • $XDG_RUNTIME_DIR/weft/, when XDG_RUNTIME_DIR is set (Linux);
  • os.UserCacheDir()/weft/ (macOS, Windows, bare containers).

The writer drops a .gitignore ("*") into ./.weft when there is none, so neither the file nor the token beside the database is committed.

The reader walks the same order and takes the first file it trusts. A file is trusted only when it is a regular file of at most 64 KiB, its url names a loopback host (127.0.0.0/8, ::1, localhost), it is fresh (started less than 24 hours ago) and its pid is alive — and, on unix, it is mode 0600 and owned by the reading user (a file from a git checkout is 0644: never trusted). Anything else is skipped with one Debug line, and the next writer removes it.

Two live Studios never erase each other's file: a writer that displaces another live Studio's file restores it when it exits.

The file is a convenience, never a requirement: WEFT_STUDIO_URL always wins (the reader does not look), WEFT_DISCOVERY=off turns the read off, and runtime.Studio(url, tok) / otel.Studio(url, tok) name a Studio in code (otel.Studio also switches the read off). The core never imports this package.

Index

Constants

View Source
const FileName = "studio.json"

FileName is the discovery file's name in each location.

View Source
const MaxAge = 24 * time.Hour

MaxAge is how old a file may be and still be trusted.

Variables

Dirs is the location order, writer and reader alike. A variable so a test points every reader and writer of its process at a temporary directory — no test touches the developer's real locations.

Functions

func DefaultDirs

func DefaultDirs() []string

DefaultDirs is the documented order: ./.weft when it exists, then $XDG_RUNTIME_DIR/weft when XDG_RUNTIME_DIR is set, then os.UserCacheDir()/weft. Paths are absolute. ./.weft is relative to the working directory: an app finds a Studio's ./.weft file only when run from the directory that Studio was started in (else WEFT_STUDIO_URL names it).

func Find

func Find(url string) string

Find is the path of a trusted file naming url, "" when no location holds one: how a start checks that the Studio on an address is the one this user's discovery file names (the probe's token, a reuse's missing-file line).

func GuardDir

func GuardDir(dir string)

GuardDir drops a .gitignore ("*") into dir when dir is a .weft directory without one — the project's local data (the database, its token, the discovery file) is never committed by accident. An existing .gitignore is never touched; failures are ignored (the guard is a courtesy, not a gate).

func Stale

func Stale(info Info) string

Stale reports why info is not to be trusted ("" when it is): its url is not loopback, its pid is not alive, or it started MaxAge or more ago (or in the future).

Types

type Info

type Info struct {
	// URL is the Studio's base URL, http://host:port (loopback).
	URL string `json:"url"`
	// Token is the Studio's API token (the file is 0600).
	Token string `json:"token"`
	// DB is the Studio's SQLite file, absolute; "" for a database with
	// no file.
	DB string `json:"db"`
	// PID is the Studio process's id.
	PID int `json:"pid"`
	// Started is when the Studio started (RFC 3339 on the wire).
	Started time.Time `json:"started"`
	// Version is the weft version the Studio runs (version.Runtime).
	Version string `json:"version"`
}

Info is the discovery file's content.

func Lookup

func Lookup(getenv func(string) string, log *slog.Logger) (info Info, path string, ok bool)

Lookup is the reader the app side uses: the first trusted file of the location order, with its path. It does not look when getenv("WEFT_STUDIO_URL") is set (the explicit Studio always wins) or getenv("WEFT_DISCOVERY") is "off". A missing file says nothing; an untrusted one (malformed, stale, not loopback, not 0600 or not this user's) is one Debug line on log (nil: slog.Default()) — observability never changes behaviour, and a leftover file is no news. ok is false when nothing trusted was found.

type Written

type Written struct {
	// Path is the file.
	Path string
	// Info is what was written.
	Info Info
	// contains filtered or unexported fields
}

Written is a discovery file this process wrote.

func Write

func Write(info Info) (*Written, error)

Write writes info to the first location (created 0700 when missing; a .weft directory gets its .gitignore) and returns the handle Remove takes. Untrusted files in every location are removed first — the writer is the one that cleans up. The write is atomic (a temporary file renamed over the old one) and the file is 0600: it carries the token. Another live Studio's file it displaces is restored by Remove. The guarantee is for two live Studios: only the one displaced file is remembered (with three, the first can be dropped), and Remove's check-then-write is not atomic against a third writer.

func (*Written) Refresh

func (w *Written) Refresh() error

Refresh re-stamps the file's started time with now, while the file is still this Studio's: a Studio running longer than MaxAge stays discoverable (the writer calls it hourly). A missing file — removed by hand, or by another writer's cleanup racing this Studio's rename — is written again, the same content and 0600 atomic rename as Write. A file another Studio has since written is left alone. Nil-safe.

func (*Written) Remove

func (w *Written) Remove()

Remove removes the file when it is still this Studio's (same pid and url) — putting back the live Studio it displaced, if any, its started time re-stamped (that Studio refreshes its own file from then on): of two live Studios, neither erases the other's file. Nil-safe.

Jump to

Keyboard shortcuts

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