registry

package
v0.15.0 Latest Latest
Warning

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

Go to latest
Published: May 10, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Index

Constants

View Source
const (
	LockFileName    = "hub.lock"
	LockPidFileName = "hub.lock.pid"
)

LockFileName is the basename of the workspace-scoped lock file `bones hub start` acquires before any port bind, URL-file write, or hub bootstrap. Lives at <root>/.bones/hub.lock alongside the URL files and pid files (per the issue brief: "workspace-scoped lock file path lives under the workspace's bones-state directory"). The sibling hub.lock.pid records the holder for human-readable error messages.

Variables

View Source
var ErrNotFound = errors.New("registry: entry not found")

ErrNotFound is returned by Read when no entry exists for the given cwd.

View Source
var HealthTimeout = 500 * time.Millisecond

Functions

func AcquireWorkspaceLock added in v0.9.0

func AcquireWorkspaceLock(root string) (func(), error)

AcquireWorkspaceLock takes an exclusive non-blocking advisory lock on <root>/.bones/hub.lock. Used by `bones hub start` to refuse a second concurrent start against the same workspace before any side effects (port bind, URL-file write, fork-exec).

Returns a release func that drops the lock + removes the sibling pid file. If the lock is held by a live process, returns *ErrLockHeld. If the sibling pid file names a dead process, the stale pid is overwritten and the lock is reclaimed (the kill -9 recovery path from the issue's acceptance criteria).

Cross-platform: on Unix this is syscall.Flock(LOCK_EX|LOCK_NB); on Windows it falls back to a best-effort pid-file probe (see lock_windows.go) since flock is not available there.

func EntryPath

func EntryPath(cwd string) string

EntryPath returns the absolute path of the JSON file for a given workspace cwd. The PID is no longer encoded in the filename (issue #250); the workspace registry is single-file-per-workspace and last-writer-wins. Per-host orphan visibility is now provided by `bones status --all` (issue #264) which scans the host process table — superseding the duplicate-detection role the PID-suffix originally played in #208.

func IsAlive

func IsAlive(e Entry) bool

IsAlive returns true if BOTH (a) the recorded HubPID is alive on this host AND (b) GET <HubURL> succeeds at the TCP/HTTP level within HealthTimeout. Both checks are required because a recycled PID can pass (a) but fail (b).

The HTTP probe doesn't require any specific endpoint — any HTTP response (including 4xx) means the port is bound and serving, which is what we actually want to know. The Fossil HTTP server bones uses doesn't expose a /health endpoint and we deliberately don't add a sidecar HTTP server just for the probe.

func IsOrphan added in v0.7.0

func IsOrphan(e Entry) bool

IsOrphan reports whether e represents a process that is alive on this host but whose workspace is no longer reachable. Three signals qualify a workspace as gone:

  1. e.Cwd does not exist on disk (ENOENT)
  2. e.Cwd exists but its workspace marker (.bones/agent.id) does not
  3. e.Cwd resolves into the user's Trash (~/.Trash on macOS, the XDG-Trash equivalent on Linux)

The PID-alive check is the same one IsAlive uses; an entry whose PID is dead is not an orphan (it's a stale entry that the read- time prune will delete; see prune.go).

Since #229's read-time self-prune, signal (1) and the dead-PID case are removed at the registry layer before IsOrphan is reached. IsOrphan still tests them defensively in case a new caller routes around List/Orphans, but in practice this function returns true only for the marker-missing or trashed-cwd signals.

func LockPath added in v0.9.0

func LockPath(root string) string

LockPath returns the absolute path of the workspace lock file for the workspace at root. Honors BONES_DIR (issue #291) when set.

func LockPidPath added in v0.9.0

func LockPidPath(root string) string

LockPidPath returns the absolute path of the sibling pid file recording the lock holder. Honors BONES_DIR when set.

func Reap added in v0.7.0

func Reap(e Entry) error

Reap terminates the process for e and removes its registry entry. SIGTERM first; if the PID is still alive after reapGrace, SIGKILL. Returns nil on success (process gone, entry removed) or an error describing which step failed. The entry is removed even after SIGKILL — leaving it would create a permanent registry record for a process that, by definition, isn't going to come back.

func Register added in v0.13.0

func Register(cwd, name string) error

Register persists a PID=0 "registered but idle" entry for cwd (#305). Used by `bones up` so the workspace is visible to `bones status --all` between `bones up` and the first verb that triggers a hub serve.

Idempotent: if an entry already exists with HubPID > 0 (a live or recently-live hub), Register is a no-op so the hub-written record is preserved. If the existing entry is a PID=0 register row, it's refreshed with the new name and timestamp.

`bones down` removes entries via Remove; a hub start overwrites the idle row with a PID-bearing entry via Write.

func RegistryDir

func RegistryDir() string

RegistryDir returns the directory that holds workspace entry files.

func Remove

func Remove(cwd string) error

Remove deletes the registry entry for the given workspace cwd — the canonical <id>.json plus any legacy <id>-<pid>.json files left over from pre-#250 layouts. Idempotent. Used by `bones down` and stale-entry pruners that operate at workspace granularity.

func WorkspaceID

func WorkspaceID(cwd string) string

WorkspaceID returns a deterministic 16-hex-char identifier for an absolute cwd. Used as the registry filename prefix: ~/.bones/workspaces/<id>.json.

func Write

func Write(e Entry) error

Write persists e to its file atomically (tmp+rename). Creates the registry directory if missing. Filename is keyed by cwd alone — see EntryPath. Last-write-wins on the file is the intended behavior in the supervisor model (one hub per workspace).

Types

type Entry

type Entry struct {
	Cwd       string    `json:"cwd"`
	Name      string    `json:"name"`
	HubURL    string    `json:"hub_url"`
	NATSURL   string    `json:"nats_url"`
	HubPID    int       `json:"hub_pid"`
	StartedAt time.Time `json:"started_at"`
}

Entry is one workspace's registry record. Each `bones hub start` writes its own entry at ~/.bones/workspaces/<WorkspaceID>.json — one file per workspace (the supervisor model). Older bones binaries used the per-PID layout `<id>-<pid>.json` (pre-#250); the read path migrates legacy files into the canonical name silently on first read.

func List

func List() ([]Entry, error)

List returns all registry entries, skipping corrupt files.

Self-prunes stale entries on read (#229): any entry whose HubPID is not alive on this host OR whose Cwd no longer exists is deleted from disk before the surviving set is returned. ADR 0043 promises the registry "prunes on read"; pre-#229 only the in-memory filter honored that — the on-disk files accumulated indefinitely.

func Orphans added in v0.7.0

func Orphans() ([]Entry, error)

Orphans returns all registry entries whose process is alive but whose workspace is gone. Read-only; the caller decides what to do.

func Read

func Read(cwd string) (Entry, error)

Read loads the workspace's registry entry. Single-file layout (#250): the canonical path is <id>.json. Legacy per-PID files (`<id>-<pid>.json`) are migrated on read into the canonical name and then deleted — silent, best-effort. If multiple legacy entries exist the alive-PID one wins (or the most recent if all are dead). If the canonical file AND legacy files both exist (a half-migrated workspace) the canonical file is preferred and the legacy files are removed.

Self-prunes the workspace's entry on read (#229): if the chosen entry's HubPID is dead OR its Cwd no longer exists, the file is removed before returning ErrNotFound.

type ErrLockHeld added in v0.9.0

type ErrLockHeld struct {
	PID  int
	Path string
}

ErrLockHeld is returned by AcquireWorkspaceLock when another live process holds the workspace lock. The PID is the holder (parsed from hub.lock.pid); callers surface it in CLI output so the operator can act.

func (*ErrLockHeld) Error added in v0.9.0

func (e *ErrLockHeld) Error() string

type HubProcess added in v0.11.0

type HubProcess struct {
	PID   int
	ETime string // raw ps "elapsed" column, e.g. "2-14:05:09" or "19:23"
	Cwd   string // absolute path, or "" when undiscoverable
	Cmd   string // full command line as ps reported it
}

HubProcess is a live `bones hub start` process discovered by scanning the host process table. Used by `bones status --all` to surface orphan hubs (#264): hubs whose PID is alive but whose workspace cwd is missing, isn't in the registry, or whose registry entry doesn't match the live PID.

Cwd is best-effort. On Linux it's read from /proc/<pid>/cwd; on macOS via `lsof -p <pid> -d cwd`. If neither path resolves the cwd (process exited, sandboxed, lsof unavailable), Cwd is left empty and the renderer surfaces it as "unknown" rather than dropping the row.

func LiveHubProcesses added in v0.11.0

func LiveHubProcesses() ([]HubProcess, error)

LiveHubProcesses scans the host for running `bones hub start` processes and returns them with parsed cwd, pid, etime, and the original command. Best-effort: a process whose cwd is unreadable still appears in the result with Cwd == "". A `ps` failure (e.g. binary missing, sandboxed) returns an error; callers may render only Section 1 and continue.

type HubStatus added in v0.8.0

type HubStatus string

HubStatus is a coarse liveness label produced by ListInfo. Values:

HubRunning  — registry recorded a hub PID, the PID is alive on this host,
              and a quick TCP/HTTP probe of HubURL returned a response.
HubStopped  — registry has an entry, but the hub is not reachable
              (PID dead OR HTTP probe failed).
HubUnknown  — the entry has no enough info to probe (e.g. missing
              HubURL/HubPID), or a probe was deliberately skipped.
const (
	HubRunning HubStatus = "running"
	HubStopped HubStatus = "stopped"
	HubUnknown HubStatus = "unknown"
)

type Info added in v0.8.0

type Info struct {
	Entry

	ID          string    `json:"id"`
	AgentID     string    `json:"agent_id"`
	HubStatus   HubStatus `json:"hub_status"`
	LastTouched time.Time `json:"last_touched"`
}

Info is one workspace registry record enriched with the on-disk filename (ID), file mtime (LastTouched), the workspace's agent.id marker (when present), and a coarse hub liveness label.

ID is the hex string used as the registry filename: ~/.bones/workspaces/<ID>.json. It equals WorkspaceID(Cwd).

func ListInfo added in v0.8.0

func ListInfo() ([]Info, error)

ListInfo enumerates every registry entry, attaching ID + mtime + agent.id + hub-status. Corrupt or unreadable files are skipped (matching List). The hub-status probe runs IsAlive on each entry; callers that want to skip the probe (e.g. for fast paths) should use List + their own enrichment.

Self-prunes stale entries on read (#229) — same predicate as List(): a dead HubPID or a missing Cwd both qualify the entry as crud and the file is removed before this function returns.

Results are sorted by Name, then Cwd, for stable output across calls.

Jump to

Keyboard shortcuts

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