runtime

package
v0.35.0 Latest Latest
Warning

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

Go to latest
Published: Oct 6, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Overview

Package runtime owns the local OVDB server's process lifecycle and the files in the runtime directory: server.json, the instance secret, the home lock and server.log. It starts the server detached, proves a running server is this home's instance (authenticated whoami), and stops it without ever signalling a process it cannot identify.

It knows nothing about HTTP routes beyond the two lifecycle endpoints (whoami and shutdown) and nothing about configuration files: callers pass the resolved port and the command that runs the server.

See spec/features/local-server-and-web-console (Lifecycle, Port) and decision 0007.

Index

Constants

View Source
const (
	WhoamiPath   = "/api/local/v1/whoami"
	ShutdownPath = "/api/local/v1/server/shutdown"
)

The lifecycle endpoints of the local API.

View Source
const (
	RecordFile = "server.json"
	SecretFile = "secret"
	LockFile   = "home.lock"
	LogFile    = "server.log"
	// StartLockFile serializes starts and locked writes (see lockStart).
	StartLockFile = "start.lock"
)

File names inside the runtime directory.

View Source
const DefaultPort = 6832

DefaultPort spells OVDB on a phone keypad.

View Source
const DefaultTimeout = 10 * time.Second

DefaultTimeout bounds readiness after start and port release after stop.

Variables

View Source
var ErrAlreadyRunning = errors.New("another OVDB server holds the home lock")

ErrAlreadyRunning reports that another server holds this home's lock.

Functions

func BaseURL

func BaseURL(port int) string

BaseURL is the address clients use: always 127.0.0.1, never a name that needs resolving (REQ:client-values-and-mismatch).

func CheckMatch

func CheckMatch(record *Record, dirs paths.Dirs, port int, explicitPort bool) *envelope.Error

CheckMatch fails with server_config_mismatch when a client whose home or explicitly chosen port differs from the running server would otherwise silently talk to — or start next to — the wrong server.

func CheckPort

func CheckPort(ctx context.Context, port int, listen ListenFunc) *envelope.Error

CheckPort reports whether a server could start on port, without keeping it. A port with anything answering on either loopback address is in use even where the OS would let a more specific bind succeed next to a wildcard listener (macOS with SO_REUSEADDR).

func IsUnconfirmedProcess

func IsUnconfirmedProcess(err error) bool

IsUnconfirmedProcess reports whether err is Stop refusing to touch a pid it could not identify; for a restart that means "not running".

func Listen

func Listen(port int, listen ListenFunc) ([]net.Listener, *envelope.Error)

Listen binds 127.0.0.1:port and [::1]:port (REQ:loopback-bind). When ::1 cannot be bound because IPv6 is unavailable the server continues on IPv4; when either address is taken it is a conflict, never a silent IPv4-only server next to an impostor.

func LogPath

func LogPath(runtimeDir string) string

LogPath is server.log in runtimeDir.

func NewHTTPClient

func NewHTTPClient(timeout time.Duration) *http.Client

NewHTTPClient returns a client for the local API that never follows a redirect: the local API does not redirect, and following one would resend the bearer secret to wherever an impostor points.

func PortInUse

func PortInUse(port int) *envelope.Error

PortInUse is the port_in_use failure with both fixes, never an automatic port change (REQ:deterministic-port-conflict).

func PortUnavailable

func PortUnavailable(port int, cause error) *envelope.Error

PortUnavailable is the port_unavailable failure for a bind the OS refuses with no listener.

func PrepareDirs

func PrepareDirs(dirs paths.Dirs, failure string) (warnings []string, err *envelope.Error)

PrepareDirs creates the OVDB home and runtime directory owner-only when they are missing. An existing directory is never changed: when it is accessible to other users, a warning with the fix is returned, and for the runtime directory — where the instance secret lives — the result is also a forbidden error, so no secret is ever written there.

failure is the message of the resulting error ("Couldn't start the OVDB server", "Couldn't change the setting").

See REQ:owner-only-state.

func ReadSecret

func ReadSecret(runtimeDir string) (string, error)

ReadSecret returns the instance secret, or "" when there is none.

func WithHomeLock

func WithHomeLock(ctx context.Context, dirs paths.Dirs, failure string, fn func() error) ([]string, error)

WithHomeLock runs fn as the home's single writer while no server runs. It returns ErrAlreadyRunning when a server holds home.lock; the caller then goes through that server instead. failure is the message of a directory error; warnings about open directories are returned in every case.

Types

type Instance

type Instance struct {
	Dirs       paths.Dirs
	InstanceID string
	Secret     string
	Version    string
	// contains filtered or unexported fields
}

Instance is the server side of the runtime directory: it holds home.lock for the server's lifetime and owns this run's instance id and secret.

func Acquire

func Acquire(dirs paths.Dirs, version string) (*Instance, []string, error)

Acquire takes this home's lock and writes a fresh instance secret, replacing any stale server.json and secret a crashed server left behind (REQ:single-server-home-lock, REQ:stale-runtime-state). Warnings about directories accessible to other users are returned even on success.

func (*Instance) NewRecord

func (i *Instance) NewRecord(port int, startedAt time.Time) (Record, error)

NewRecord describes this process serving port since startedAt.

func (*Instance) Publish

func (i *Instance) Publish(record Record) error

Publish writes server.json once the server is listening. Clients treat its appearance, together with an authenticated whoami, as readiness.

func (*Instance) Release

func (i *Instance) Release()

Release removes server.json and the secret and unlocks the home. It is safe to call more than once.

type ListenFunc

type ListenFunc func(network, address string) (net.Listener, error)

ListenFunc binds a listener; net.Listen satisfies it. Tests inject one to simulate an OS that refuses a bind (for example a Windows reserved range).

type Record

type Record struct {
	Schema     int    `json:"schema"`
	InstanceID string `json:"instance_id"`
	Home       string `json:"home"`
	PID        int    `json:"pid"`
	// ProcessIdentity is daemonlifecycle.ProcessIdentity(PID) at start; stop
	// signals PID only while it still matches.
	ProcessIdentity string    `json:"process_identity"`
	Port            int       `json:"port"`
	Version         string    `json:"version"`
	StartedAt       time.Time `json:"started_at"`
}

Record is server.json: written by a server once it is listening, it tells clients where the server is and which process it is.

func ReadRecord

func ReadRecord(runtimeDir string) (*Record, error)

ReadRecord returns the runtime record, or nil when there is none.

type StartOptions

type StartOptions struct {
	Dirs         paths.Dirs
	Port         int
	ExplicitPort bool // --port or OVDB_PORT, as opposed to config or default
	// Command returns the template of the process that serves port. Start
	// adds the working directory (OVDB home) and the directory variables.
	Command func(port int) *exec.Cmd
	Timeout time.Duration // DefaultTimeout when zero
	Listen  ListenFunc    // net.Listen when nil
}

StartOptions describe one start.

type StartResult

type StartResult struct {
	State          State
	AlreadyRunning bool
	Warnings       []string // directories other users can access
}

StartResult reports a successful start or an already running server.

func Start

func Start(ctx context.Context, opts StartOptions) (StartResult, error)

Start starts the local server detached and waits for authenticated readiness (REQ:background-start). It succeeds without starting anything when this home's server already runs, fails with server_config_mismatch when a different home or port runs, and fails with port_in_use, port_unavailable, forbidden or server_start_failed otherwise. Warnings are returned on failure too.

type State

type State struct {
	Running bool
	Record  *Record // nil when there is no readable server.json
	Secret  string
	Whoami  *Whoami // the running server's answer
	// Unreadable is set when server.json exists but cannot be read; it is
	// treated as stale, and the next start replaces it.
	Unreadable error
}

State is what the runtime directory says about the server, confirmed by an authenticated whoami.

func Inspect

func Inspect(ctx context.Context, runtimeDir string) (State, error)

Inspect reads server.json and the secret and asks the recorded port who is there. A record whose server does not answer as the recorded instance, or that cannot be read at all, is stale: the state is "not running" (REQ:stale-runtime-state).

type StopResult

type StopResult struct {
	WasRunning bool
	Forced     bool // the server did not answer and its verified process was terminated
	Stale      bool // runtime files named a server that is gone; they were cleared when possible
	Record     *Record
}

StopResult reports what Stop did.

func Stop

func Stop(ctx context.Context, runtimeDir string, timeout time.Duration) (StopResult, error)

Stop shuts the server down through the authenticated API and waits until its process is gone. If the server does not answer, the recorded pid is terminated only while its process identity still matches server.json. Otherwise no process is touched: when home.lock is free no server can be alive, so the stale runtime files are cleared and the result is "not running"; when something holds the lock, the failure says the process could not be confirmed (REQ:authenticated-stop, AC:stop-never-kills-reused-pid, REQ:stale-runtime-state).

type Whoami

type Whoami struct {
	Schema     int    `json:"schema"`
	InstanceID string `json:"instance_id"`
	Version    string `json:"version"`
}

Whoami is the body of GET /api/local/v1/whoami.

func Probe

func Probe(ctx context.Context, port int, secret string) (*Whoami, error)

Probe calls the authenticated whoami on port. Sending the secret to an impostor is harmless: every start writes a new secret, and a server that holds this port is by construction not the one the secret belongs to.

Jump to

Keyboard shortcuts

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