Documentation
¶
Overview ¶
Package runtime is the seam between brig's product logic and whatever actually runs the container.
brig is not a container runtime and does not want to be one. It adds workspace-as-home, host-resolved credentials with per-exec forwarding, the billing denylist and the git plumbing, and delegates every mechanical operation -- boot this image, exec in it, stop it -- to hull on macOS and nerdctl on Linux. Both sit behind this one interface, so the logic above it is written once and neither OS gets a forked copy.
Index ¶
- Variables
- func ArgvExposed(vars []Var) []string
- func BootAssetNames() (kernel, initrd string)
- func BootAssetsDir(rt Runtime) (dir string, present, explicit bool, err error)
- func BootAssetsRef() string
- func BootedNet(name string) (string, error)
- func ForgetBootedNet(name string)
- func ForgetPublications(name string)
- func GatewayLogPath() (string, error)
- func IsolatedGatewayLogPath(name string) (string, error)
- func PruneBootedNets(keep []string)
- func PrunePublications(keep []string)
- func RecordBootedNet(name, net string) error
- func RunAttached(argv, env []string) (int, error)
- func Version(bin string) (string, error)
- func VersionToken(out string) string
- type BootAssets
- type BootFetch
- type BootResolver
- type Boundary
- type Capability
- type CapabilityError
- type Egress
- type ExecSpec
- type Exister
- type FallbackReporter
- type FeedLimiter
- type Instance
- type Isolation
- type LogsSpec
- type NetworkChecker
- type NetworkInspector
- type NetworkPruner
- type Preference
- type Property
- type Publication
- func ForgetSomePublications(name string, drop []Publication) (left, gone []Publication, err error)
- func ParseHostSide(s string) (Publication, error)
- func ParseHostSides(specs []string) ([]Publication, error)
- func ParsePublication(s string) (Publication, error)
- func ParsePublications(specs []string) ([]Publication, error)
- func Publications(name string) ([]Publication, error)
- func RecordPublications(name string, add []Publication) ([]Publication, error)
- func (p Publication) Addr() string
- func (p Publication) Addressed() bool
- func (p Publication) Line() string
- func (p Publication) Local() string
- func (p Publication) Matches(q Publication) bool
- func (p Publication) Overlaps(q Publication) bool
- func (p Publication) Proto() string
- func (p Publication) Remote(guestIP string) string
- func (p Publication) Same(q Publication) bool
- func (p Publication) String() string
- type Publisher
- type Record
- type Rule
- type RunChecker
- type RunPath
- type RunSpec
- type Runtime
- type Share
- type SharedNetworkPruner
- type Spinner
- type Var
Constants ¶
This section is empty.
Variables ¶
var ErrBadRuntime = errors.New("runtime unavailable")
ErrBadRuntime is the other DetectFor failure: a runtime was asked for and is wrong, rather than absent. An unknown BRIG_RUNTIME, or a runtimeBin that is not on disk or is not executable, wrap this. It is kept apart from ErrNoRuntime because the two want different handling -- env and ls degrade over ErrNoRuntime and still fail over this -- and because a caller mapping a failure to an exit code treats "you have no runtime" and "the runtime you named is broken" as the same class to fix, so both reach it with errors.Is.
var ErrNoForwardAPI = errors.New("this runtime's gateway cannot publish a port")
ErrNoForwardAPI is what a gateway too old to publish a port answers with. It is kept apart so the caller can say which half of the install is behind rather than quoting a 404 at someone.
var ErrNoRuntime = errors.New("no runtime found on PATH")
ErrNoRuntime is the one DetectFor failure that means "nothing is installed" rather than "you asked for the wrong thing": no runtime binary was on PATH and none was pinned. env and ls degrade to it -- report what they can and exit 0 -- and they must key off this with errors.Is, never off "err != nil". An unknown BRIG_RUNTIME or a runtimeBin that is not there is a mistake to fix, and env is the verb people run to find it, so those still surface with a non-zero exit even there. Matching the sentinel rather than any error is what stops the day the message or type changes from silently widening the swallow.
Functions ¶
func ArgvExposed ¶
ArgvExposed names the variables whose values BRIG_ENV_ARGV puts on the runtime's command line, in the order they were given. It returns nothing when the hatch is off, and never names a Var marked Secret: splitEnv keeps those off the command line whatever the setting says.
It does not name HOME, PATH, TMPDIR or an XDG_ variable. Those are on the command line on every run, hatch or not, and none of them is a credential. Naming HOME on every run would bury the names this report is for.
Exported so the exposure can be said out loud before the runtime is invoked. BRIG_ENV_ARGV is opted into once, in a shell profile, and then remembered by nobody: months later such a run looks like every other one, and the only places the difference shows are `ps` and the host's own argv log, neither of which anyone reads until after something has gone wrong. The names are what a caller can act on; the values stay here.
func BootAssetNames ¶ added in v0.3.0
func BootAssetNames() (kernel, initrd string)
BootAssetNames are the two files a boot bundle must hold, for a message that tells someone what to put in a directory.
func BootAssetsDir ¶
BootAssetsDir reports the directory the boot bundle for this host lives in and whether the kernel and initrd are already there, so brig doctor can say "present" or name the directory that is empty. It resolves the directory through bootAssetsDir, the same function the boot path uses, with the same question put to rt: a hull is asked with `hull assets dir`, and a runtime with no opinion gets the per-platform default. So doctor checks the directory a run would boot from, not a guess at it (#314).
It reports presence rather than fetching: doctor names what is missing and how to get it, and downloading a bundle is a side effect a diagnostic has no business having.
explicit reports that BRIG_BOOT_ASSETS chose the directory, which a run never downloads into, so doctor's advice for an empty one differs.
func BootAssetsRef ¶
func BootAssetsRef() string
bootAssetsRef is the bundle for this host.
The tag names the guest platform. On Linux that is also the host's -- an amd64 box boots an amd64 guest -- so GOOS-GOARCH is the right key. BRIG_BOOT_ASSETS_REF overrides it whole, which is how a version gets pinned or a mirror gets used. BootAssetsRef is the bundle this host would boot, exported so the layer that owns BRIG_VERIFY can name what it is checking. The fetch happens in a runtime adapter, through BootResolver, but whether to trust what is fetched is a policy question and belongs where the other one is answered.
func BootedNet ¶ added in v0.3.0
BootedNet is the network this sandbox was last booted on, or "" when none is recorded: a sandbox booted by an older release, or one never booted.
func ForgetBootedNet ¶ added in v0.3.0
func ForgetBootedNet(name string)
ForgetBootedNet drops the record of a removed sandbox, so the next sandbox to take the name starts from the ordinary resolution.
Errors are dropped, the way ForgetPublications drops its own: the removal worked, and a bookkeeping file is no reason to report that it did not.
func ForgetPublications ¶ added in v0.3.0
func ForgetPublications(name string)
ForgetPublications drops everything a removed sandbox published.
Called from `brig rm` rather than from the adapter's Remove, which the run path also calls: a sandbox recreated because a share went stale is the same sandbox, and it has to come back offering the same ports.
Best effort, like releasing an address: the record costs a few bytes and nothing about removing a sandbox depends on it. The forwards themselves go with the gateway that served them.
func GatewayLogPath ¶
GatewayLogPath is where the shared gateway's own output is written, beside its socket, and IsolatedGatewayLogPath is the same for the gateway serving one sandbox alone. ensureGateway and ensureIsolatedGateway open them, and `brig logs --gateway` points at them: the gateway logs a boot's network failure to a file no command named until now, so a network that never came up was invisible unless you already knew the file was there. Both are derived from the socket by the same rule startGateway uses, so the writer and the reader cannot drift onto two paths.
The shared log stays put; an isolated one does not. clearGatewayRecord removes it with the gateway it belongs to, once that gateway is confirmed stopped, which is what keeps ~/.brig from growing a file for every sandbox ever isolated. So an isolated log is there while its gateway runs -- and after one that failed to start, which is the case a reader most needs -- and gone once the sandbox is stopped.
func IsolatedGatewayLogPath ¶
func PruneBootedNets ¶ added in v0.3.0
func PruneBootedNets(keep []string)
PruneBootedNets drops the record of every sandbox not in keep.
`brig ls` and `brig rm --all` call it with what the runtime still holds. A sandbox removed by an older release, or with the runtime's own CLI, leaves its record behind, and the next sandbox to take the name would inherit it.
func PrunePublications ¶ added in v0.3.0
func PrunePublications(keep []string)
PrunePublications drops the record of every sandbox not in keep.
`brig rm --all` calls it with the sandboxes that survived the removal. A record can also belong to a sandbox that never booted, because `brig network publish` records a port for one. Left behind, it would open that port on the next sandbox to take the name.
func RecordBootedNet ¶ added in v0.3.0
RecordBootedNet records the network this sandbox has just booted on.
Called after a boot and nowhere else. A sandbox that is already running was not booted by this command, and recording what the command asked for would write down a network the runtime was never told about.
func RunAttached ¶
RunAttached runs argv as a child of brig, inheriting brig's own stdin, stdout and stderr, waits for it, and returns its exit status. argv[0] is the binary; env is the full child environment. Both adapters build the pair from the very function their Replace uses, so the child and the process replacement cannot drift.
It is the child-process counterpart of syscall.Exec. Replace hands the terminal, the signals and the exit status straight to the runtime and never returns, which is the right default: the agent's TUI wants a real tty with nothing between it and the keyboard. Attach keeps brig alive across the exec so brig can report the outcome on a machine-readable line afterwards, which Replace by definition cannot -- there is no brig left once the exec has happened.
A child killed by a signal is reported as 128 plus the signal number, the convention a shell follows, so a caller mapping the status onto its own exit returns the same number a shell would for the same death.
func Version ¶
Version runs `<bin> --version` and returns what it prints, trimmed.
A package-level helper rather than a Runtime method on purpose. The Runtime interface is being changed by another issue in parallel, and brig doctor is the only caller that wants the version string, so a free function keeps the two from colliding -- and PinsDigest already shells the same command for a bool, so nothing new is learned about the binary that was not learned before.
func VersionToken ¶ added in v0.3.0
VersionToken returns the version out of a runtime's `--version` line, without its leading v, or "" when the line carries none.
The version is the first word shaped like one, not a fixed position. hull up to 0.1.0-rc28 printed `hull version 0.1.0-rc28`, with the version last. From the release that reports its build it prints `hull v0.1.0-rc29 (4f5b4cb, 2026-09-24, go1.26.5, darwin/arm64)`, where the last word is the platform. nerdctl prints `nerdctl version 2.3.5`. A build from source can print `hull dev (...)`, which has no version word, and whose date must not be read as one.
Types ¶
type BootAssets ¶ added in v0.4.0
type BootAssets struct {
Kernel string
Initrd string
// Named reports that BRIG_BOOT_ASSETS chose the directory. A directory
// someone named is their build, and wrap weighs a digest that differs
// from the verified bundle differently there (#234).
Named bool
}
BootAssets are the host paths of the kernel and initrd a GenericBoot run boots.
type BootFetch ¶ added in v0.4.0
type BootFetch struct {
// Ref is the boot bundle to fetch, pinned to the digest whose signature
// brig verified, repo@sha256:... Fetching the tag instead lets a tag that
// moved after the check deliver a bundle nobody checked. Empty fetches the
// tag BootAssetsRef names, for a bundle brig had no signature to check.
Ref string
// Replace fetches even when both files are there, over them. wrap asks for
// it when the files are an older bundle's, in a directory brig chose.
Replace bool
}
BootFetch is what a resolve fetches, and when.
type BootResolver ¶ added in v0.4.0
type BootResolver interface {
// ResolveBootAssets returns the paths for RunSpec.BootAssets, fetching
// what fetch names when a file is missing or fetch.Replace asks. notice
// and progress are the writers a download narrates to, the same pair
// RunSpec.Notice and RunSpec.Progress carry.
ResolveBootAssets(fetch BootFetch, notice, progress io.Writer) (BootAssets, error)
}
BootResolver is a runtime that finds the kernel and initrd a GenericBoot profile boots, and fetches them when they are missing, ahead of Run.
wrap calls it before the verify summary. The fetch used to happen inside Run, after the summary had already said the boot assets verified. Moving it up puts the files on disk before the summary, which is where #234 needs them to compare their digests. Optional for the same reason NetworkChecker is. A runtime without it resolves inside Run, as before.
type Boundary ¶
type Boundary string
Boundary is what stands between the guest and the host kernel.
Three values rather than two, because "brig cannot tell" is a real answer and the only honest one for a shim brig does not recognise. A reader acts on this row -- it is the difference between running an agent on an untrusted repository and not -- so a guess in the direction of the stronger boundary is the one mistake this type must not make.
const ( // BoundaryVM is a guest with a kernel of its own, booted by a hypervisor. BoundaryVM Boundary = "microVM" // BoundaryContainer is a guest sharing the host kernel. BoundaryContainer Boundary = "container" // BoundaryUnknown is everything brig has not established either way. BoundaryUnknown Boundary = "unknown" )
type Capability ¶ added in v0.4.0
type Capability string
Capability is a run path's answer to whether it enforces a property a run asks for. Brig boots a run that needs a property only on an answer of Enforced. The other two refuse, because a sandbox that reports a policy and filters nothing is worse than no policy: someone relies on it.
const ( // Enforced means the run path enforces the property. Enforced Capability = "enforced" // CannotEnforce means the run path is known not to enforce it. CannotEnforce Capability = "cannot enforce" // Unknown means nothing confirms either answer: a run path with no // record, or a runtime whose probe failed. Unknown Capability = "unknown" )
type CapabilityError ¶ added in v0.4.0
type CapabilityError struct {
Property Property
Path RunPath
State Capability
// Why and Remedy come from the record. For a failed probe, Why names
// the probe command.
Why string
Remedy string
// Err is what went wrong while asking, such as a probe that did not
// run. Nil when the answer came from the table.
Err error
}
CapabilityError refuses a run because its run path does not enforce a property the run asks for. It names the property, the runtime and the backend, so a caller can tell this refusal from any other failure, and the person reading it knows which of the three to change.
func (*CapabilityError) Error ¶ added in v0.4.0
func (e *CapabilityError) Error() string
func (*CapabilityError) Unwrap ¶ added in v0.4.0
func (e *CapabilityError) Unwrap() error
type Egress ¶
Egress is a sandbox's outbound rule set as the runtime takes it: a default verdict and the rules that beat it. Deny beats Allow beats Default.
The same shape as policy.Egress rather than the type itself, so the runtime adapters do not depend on the policy package or on how a policy document is spelled. What each field means lives on policy.Egress, which is where anyone writing one will be reading.
type ExecSpec ¶
type ExecSpec struct {
Name string
Cmd []string
Cwd string
TTY bool
Env []Var
Counted bool
// Stdin, when set, is fed to the command inside the guest. It is how a
// credential reaches the guest without appearing in argv: hull durably
// logs every exec's argv to a host file, so a value there outlives the
// sandbox in a file the user never sees. Only Feed reads it.
Stdin io.Reader
// User runs the command as a guest user other than the image's own --
// "root", for the one privileged exec that mounts the tmpfs. Empty leaves
// the image's configured user, which is what every other exec wants.
User string
}
ExecSpec is a request to run something inside a sandbox.
type Exister ¶ added in v0.3.0
Exister is a runtime that can say whether one sandbox exists, running or stopped. Exists returns false with a nil error only when the runtime says the sandbox is not there. Any other failure is an error.
List is not enough for this: when `hull ps -a` fails, hull's List falls back to plain `hull ps`, and brig cannot tell whether that listing includes stopped sandboxes. A caller that deletes something because a sandbox is gone asks Exists. Optional: a runtime without it cannot vouch that a sandbox is gone.
type FallbackReporter ¶ added in v0.4.0
type FallbackReporter interface {
// Fallback is the one line to print, or "" when the binary is the first
// choice or a setting named it.
Fallback() string
}
FallbackReporter is a runtime that can say it settled for a binary nobody asked for.
The nerdctl adapter takes docker from PATH when nerdctl is not there. It used to do that without a word, so someone who installed brig for a microVM boundary got whatever docker's setup gives them and no brig output said so (#30). The CLI type-asserts for this after it detects a runtime and prints the note at default verbosity.
Optional on the same terms as NetworkPruner. A runtime with nothing to fall back to needs no stub to say so.
type FeedLimiter ¶ added in v0.3.0
type FeedLimiter interface {
MaxFeed() int
}
Runtime is the container mechanics brig delegates. FeedLimiter is a runtime whose Feed cannot carry more than MaxFeed bytes of stdin in one call. Optional: a runtime without the limit does not implement it, and a caller hands the whole value to one Feed.
type Isolation ¶
type Isolation struct {
Boundary Boundary
// Detail is what establishes the boundary, in the reader's terms: which
// runtime, which backend or shim, and for anything short of a microVM what
// that costs them.
Detail string
}
Isolation is the boundary a sandbox gets, and what establishes it.
It reports what this invocation resolved -- the runtime binary in hand, the hypervisor backend the run settled on, the containerd shim the run will name -- and not what a profile or the documentation says a sandbox ought to be. brig does not look inside a running guest to confirm it, and the row says nothing that depends on doing so.
type LogsSpec ¶
type LogsSpec struct {
Name string
// Follow streams the log until the reader interrupts it. brig reads a log
// here and does not own the sandbox, so an interrupt that reaches the
// runtime must leave the sandbox running -- see the note on Logs.
Follow bool
// Tail is how many trailing lines to show, or -1 for all. -1 is both
// runtimes' own default; a container runtime with no notion of a negative
// count reads it as "all" rather than passing it through.
Tail int
// Out is where the log is written, as it arrives. The caller wraps it to
// filter the terminal control sequences a runtime may have captured, unless
// asked for the raw bytes.
Out io.Writer
// Err is where the runtime's own standard error goes, kept apart from Out
// rather than merged into it. On hull that stream carries the runtime's
// diagnostics and nothing else, so merging would write "instance not
// found" into a redirect that was meant to hold a log; on a container
// runtime it carries the guest's stderr, which a reader redirecting stdout
// expects to still see on the terminal. The caller wraps this one too, so
// the control-sequence filter covers both halves of the stream.
Err io.Writer
}
LogsSpec is a request to stream a sandbox's log.
An options value rather than a parameter list, in the style of RunSpec and ExecSpec: the flags brig hands the runtime -- follow, tail -- are a set that grows, and threading them positionally is what makes an adapter and its caller drift.
type NetworkChecker ¶
type NetworkChecker interface {
// NetworkStale reports whether the running sandbox differs from what this
// run asks for. False when they agree, and when the backend has no way to
// tell -- a restart nobody needs costs a boot, but a restart that is
// skipped costs the promise.
NetworkStale(name, hypervisor, net string, e Egress) bool
}
NetworkChecker is a runtime that can say whether a sandbox already running is on the network, and under the rules, that a run is asking for.
It exists because both are fixed at boot. A gateway reads its subnet and its egress rules once, when it starts, so a sandbox that is already up cannot be moved or told a new rule -- and a run that found it running and carried on would report a policy in the execution envelope that the live sandbox is not under. That is the exact failure the rest of this is built to prevent, so it is not one to leave on the one path that skips the boot.
Optional for the same reason NetworkPruner is: a backend brig does not own the network of cannot answer, and should not grow a stub to say so. wrap asserts for it and treats a runtime that does not implement it as current.
type NetworkInspector ¶ added in v0.4.0
type NetworkInspector interface {
// SandboxNetwork returns shared, isolated or none. Empty with no error
// means the runtime confirmed the sandbox is absent; missing or unknown
// network metadata is an error, never evidence of a shared network.
SandboxNetwork(name string) (string, error)
}
NetworkInspector reads the network an existing sandbox was booted with, including a stopped sandbox. It lets sessions from before posture records keep an explicitly isolated or offline network without guessing from the old default. No record is written and no gateway is started by this read.
type NetworkPruner ¶
type NetworkPruner interface {
// PruneNetworks removes the networks brig made that none of inUse is on,
// and reports how many went.
PruneNetworks(inUse []string) int
}
NetworkPruner is a runtime that makes a network per sandbox and can tidy the ones nothing is on any more.
Optional because it is not part of running a sandbox: a backend that makes no networks should not have to grow a stub method to say so. reset asserts for it and skips a runtime that does not implement it.
type Preference ¶
type Preference struct {
// Bin is the runtime binary a profile named, if any.
Bin string
}
Preference is what a profile asked for. A setting in the environment beats it, which is brig's usual order: a flag, then BRIG_*, then the profile.
type Property ¶ added in v0.4.0
type Property string
Property is something a run asks a runtime to enforce.
const EgressPolicy Property = "egress policy"
EgressPolicy is the egress rule set a policy puts on a sandbox.
type Publication ¶ added in v0.3.0
type Publication struct {
// Protocol is tcp or udp. Empty means tcp, which is what a dev server is.
Protocol string `json:"protocol,omitempty"`
// HostAddr is the address on the host the port is offered at. Empty means
// 127.0.0.1, which keeps it on this machine.
HostAddr string `json:"hostAddr,omitempty"`
// HostPort is the port on the host, and GuestPort the port inside the
// sandbox it carries to.
HostPort int `json:"hostPort"`
GuestPort int `json:"guestPort"`
}
Publication is one guest port offered on the host.
func ForgetSomePublications ¶ added in v0.3.0
func ForgetSomePublications(name string, drop []Publication) (left, gone []Publication, err error)
ForgetSomePublications drops these from the sandbox's record and returns what is left, with the ones that were actually there.
The record alone. It is what a sandbox that is not running has -- there is no gateway to tell, and its next boot reconciles against this.
func ParseHostSide ¶ added in v0.3.0
func ParseHostSide(s string) (Publication, error)
ParseHostSide reads a port as `brig network unpublish` names one, by its host side.
It takes every spelling ParsePublication does, and ADDR:PORT as well, which is how the HOST column of `brig network ls` prints a port. That form has no guest half, so the guest port is set to the host port. Matches never compares guest ports, so the value does not change what it names.
--publish does not take ADDR:PORT. docker refuses it too, and publishing needs a guest port.
func ParseHostSides ¶ added in v0.3.0
func ParseHostSides(specs []string) ([]Publication, error)
ParseHostSides is ParsePublications for the ports `brig network unpublish` names. See ParseHostSide.
func ParsePublication ¶ added in v0.3.0
func ParsePublication(s string) (Publication, error)
ParsePublication reads one published port, as --publish takes it and as `brig network publish` takes its bare words.
The grammar is docker's, and deliberately: this is the one part of brig a person arrives at already knowing.
3000 the same port on both sides, on loopback 8080:80 a host port and the guest port behind it 127.0.0.1:8080:80 an explicit host address 0.0.0.0:8080:80 offered beyond this machine 5353:53/udp a protocol other than tcp
Refused rather than guessed at, on the rule the security switches follow: a typo in the address half of this decides who can reach the sandbox, so it has to stop the run.
func ParsePublications ¶ added in v0.3.0
func ParsePublications(specs []string) ([]Publication, error)
ParsePublications reads every published port on a command line, and refuses two that would want the same host listener.
The duplicate is caught here rather than at the gateway, because the gateway sees them one at a time and would report the second as a conflict with something already published -- which is true, and says nothing about the command line that asked for both.
func Publications ¶ added in v0.3.0
func Publications(name string) ([]Publication, error)
Publications is what this sandbox is recorded as publishing, in the order a reader should meet them: by host port.
A record that cannot be read is an error rather than an empty set. Reading it as empty is what would have a boot withdraw every port the sandbox has, on nothing worse than a transient failure to open a file.
func RecordPublications ¶ added in v0.3.0
func RecordPublications(name string, add []Publication) ([]Publication, error)
RecordPublications adds these to what the sandbox already publishes, and returns the whole set.
Adding rather than replacing. A publication is part of a sandbox's configuration and outlives one run of it, so `brig run` with no --publish on a sandbox that was published to must not silently withdraw the port. The envelope names every publication on every run, so nothing here is invisible; `brig network unpublish` is how one goes away.
A repeat of one already recorded is not an error. Two --publish flags on one line that want the same host port are, and ParsePublications catches those before this is reached; the case here is the same command run twice.
func (Publication) Addr ¶ added in v0.3.0
func (p Publication) Addr() string
Addr is the host address this publication is offered at, with the default filled in.
func (Publication) Addressed ¶ added in v0.3.0
func (p Publication) Addressed() bool
Addressed reports whether a host address was written out, rather than left to the loopback default. `brig network unpublish 8080` names a port and no address; `brig network unpublish 0.0.0.0:8080:80` names both.
func (Publication) Line ¶ added in v0.3.0
func (p Publication) Line() string
Line is the publication as the envelope says it, host side first because that is the half the reader can open.
func (Publication) Local ¶ added in v0.3.0
func (p Publication) Local() string
Local is the host side as an address, and Remote is the guest side once the guest's own address is known.
func (Publication) Matches ¶ added in v0.3.0
func (p Publication) Matches(q Publication) bool
Matches reports whether this publication is the one q names.
A publication written without an address matches any address on that port, because the person typing `brig network unpublish 8080` is naming the port they saw and cannot be asked to remember which interface it went to. One written with an address matches only that address.
func (Publication) Overlaps ¶ added in v0.3.0
func (p Publication) Overlaps(q Publication) bool
Overlaps reports whether two publications would compete for one host port. That is Same, and also a pair on one protocol and host port where either address is unspecified: 0.0.0.0 listens on every address the host has, 127.0.0.1 included.
A new publication is checked with this, so that it replaces the one it competes with rather than joining it.
func (Publication) Proto ¶ added in v0.3.0
func (p Publication) Proto() string
Proto is the protocol, with the default filled in.
func (Publication) Remote ¶ added in v0.3.0
func (p Publication) Remote(guestIP string) string
func (Publication) Same ¶ added in v0.3.0
func (p Publication) Same(q Publication) bool
Same reports whether two publications name one listener: the same protocol, host address and host port, whatever guest port they carry to.
func (Publication) String ¶ added in v0.3.0
func (p Publication) String() string
String is the publication in the longest form --publish takes, whichever of the short spellings was typed. A reader of an envelope row or an error should not have to reconstruct the address half.
type Publisher ¶ added in v0.3.0
type Publisher interface {
// Publish offers a guest port on the host, and Unpublish withdraws one.
Publish(name string, p Publication) error
Unpublish(name string, p Publication) error
// Published is what this sandbox is offering, as the gateway serving it
// reports, not as brig recorded it. The two agreeing is the thing worth
// checking, so this asks the side that would be wrong.
Published(name string) ([]Publication, error)
}
Publisher is a runtime that can publish a guest port on the host while the sandbox runs, and take the publication away again.
Optional, on the same terms as NetworkPruner. A container runtime fixes its published ports when the container is created and has no way to add one afterwards, so it implements none of this and cmd/brig says why rather than calling a stub that always fails.
type Record ¶ added in v0.4.0
type Record struct {
Property Property
Path RunPath
State Capability
// Why says what makes this the answer. A refusal quotes it.
Why string
// Remedy says what to do about a refusal on this path.
Remedy string
}
Record is one run path's answer for one property, with the reason it holds.
type Rule ¶
Rule is one destination, by exactly one of the two spellings it can take: a glob on the name the guest resolves, or a range the address is matched against.
type RunChecker ¶
type RunChecker interface {
// CanRun reports what this backend cannot honour about the run, or nil.
CanRun(spec RunSpec) error
}
RunChecker is a runtime that can refuse a run it cannot honour, before anything is started.
Separate from Run because the refusal has to reach the path where Run is never called. A sandbox that is already up is joined rather than booted, and a check that lived only inside Run would let exactly the runs it exists to stop through: a policy on a backend that cannot enforce it is refused on the first `brig run` and waved past on the second.
Optional on the same terms as NetworkPruner. A runtime that can honour anything asked of it needs no stub to say so.
type RunPath ¶ added in v0.4.0
RunPath is one runtime with one backend: hull with vz, hvi or qemu, or nerdctl or docker with its containerd shim. A property is enforced or not by the pair, and hull alone says nothing: the same binary enforces a policy on hvi and cannot on vz.
type RunSpec ¶
type RunSpec struct {
Name string
Image string
// Digest is the registry digest verify resolved and checked for Image, or
// "" when none was resolved. A runtime whose store is addressable by digest
// boots Image@Digest instead of the tag, so the bytes that boot are the ones
// that verified; one that is not ignores it and boots the tag. See
// PinsDigest, and the note on the nerdctl and hull adapters.
Digest string
Pull string // missing (default) | always | never
Net string // none | shared | isolated
Mem int // MB
CPUs int
Env []Var
GUI bool
GUITitle string
// Hypervisor is the macOS backend to boot on: vz, hvi or qemu. Empty
// means the runtime's own default. Already resolved by the caller, which
// is where a setting beats a profile; see wrap.
Hypervisor string
// RootfsType is how the guest root reaches the VM: block (an ext4 image
// the guest writes to), virtiofs or 9pfs. Empty leaves the runtime's own
// default, which is what almost every profile wants; a profile sets it
// when the sandbox installs packages and needs a real writable disk.
// Meaningless to a container runtime, which ignores it.
RootfsType string
// Tmpfs are tmpfs mounts to create with the sandbox, as
// "<path>:<options>". Only the container runtimes take these: hull has no
// create-time tmpfs, so brig mounts one there with a privileged exec
// instead. A runtime that cannot honour it ignores it, and the caller
// verifies the result in the guest either way -- which is what makes the
// asymmetry safe rather than merely tolerated.
Tmpfs []string
// GenericBoot asks the runtime to boot this image as an ordinary OCI
// image rather than one built to be a guest -- no kernel inside it, no
// urunc metadata. The runtime supplies the kernel and initrd. See
// bootArtifacts and BootAssets below.
GenericBoot bool
// BootAssets are the kernel and initrd a GenericBoot run boots, when the
// caller resolved them before Run. An adapter given both boots them as
// they are. It does not ask where the assets live and does not fetch
// them. wrap resolves them before the verify summary through
// BootResolver, so the summary follows the resolve (#234). Left empty,
// the adapter resolves them itself inside Run.
BootAssets BootAssets
// Egress is what this sandbox may open a connection to: the rules of
// every policy bound to it, merged. The zero value is unfiltered, which
// is what every sandbox got before anything read a policy at boot.
//
// Unlike the fields above it never reaches the run command line. The
// rules belong to the network gateway serving this sandbox and are read
// once, when that gateway starts, which is also why a sandbox carrying
// one needs a network of its own. See gateway.go.
Egress Egress
// Publish are the guest ports this run offers on the host.
//
// Every one of them is a hole in the sandbox boundary, so they are named
// in the execution envelope and default to loopback. What reaches a
// runtime here is the whole set the sandbox publishes, which is the
// record on disk and not only what this command line asked for: a
// publication is part of a sandbox's configuration and outlives one run.
// See publish.go.
Publish []Publication
// Counted marks a user action, whose boot and lifetime hull's telemetry
// reports, rather than brig's own plumbing. See telemetryEnv.
Counted bool
// Progress is where the runtime's own output goes: the image pull, the
// boot messages, whatever the binary underneath writes on its way up.
//
// nil holds it instead. It is captured and quoted back on the error if the
// boot fails, where it is the evidence, and dropped if it does not -- see
// narration. That is the default because the output is worth reading in
// exactly two situations, and a boot that worked is neither of them.
Progress io.Writer
// Notice is where a long operation says, in one line, that it has started
// and that it is done.
//
// Separate from Progress because the two answer different questions. The
// stream is detail and waits to be asked for; a download that takes a
// minute has to say so whether or not anyone asked, or the terminal looks
// hung. nil is silence.
//
// A Notice that implements Spinner shows the operation while it runs
// instead of printing the two lines. A writer wrapped around one prints
// the lines. See announce.
Notice io.Writer
}
RunSpec is a request to boot a sandbox.
type Runtime ¶
type Runtime interface {
// Kind is the backend name, e.g. "hull" or "nerdctl".
Kind() string
// Bin is the executable actually being driven.
Bin() string
// Isolation reports what a sandbox this runtime boots actually stands on:
// a kernel of its own, the host's, or something brig cannot establish.
//
// hypervisor is the macOS backend the run resolved to, or "" for the
// runtime's own default; a container runtime ignores it, the way it already
// ignores RootfsType. It is a parameter rather than a field because the
// backend is a property of the run and not of the binary in hand, and
// because the envelope is printed before any RunSpec exists.
//
// On the interface rather than an optional one asserted for, unlike
// NetworkPruner: every runtime knows what it gives a guest, and a backend
// that silently reported nothing would be read as a sandbox with no
// boundary worth naming.
Isolation(hypervisor string) Isolation
// Running reports whether a sandbox of this name is up, and returns an
// error when it could not find out at all.
//
// Three answers, not two, because a runtime that cannot be asked -- a binary
// that is not there, a permission error, a daemon that is down -- is not a
// runtime saying no. Both adapters used to fold the failure into false, so a
// broken runtime read as an empty machine and EnsureRunning booted a second
// sandbox onto a workspace the first was still holding (#49). A caller that
// cannot tell must not boot: false with a non-nil error means "unknown", and
// the boolean says nothing.
Running(name string) (bool, error)
// List returns every sandbox the runtime knows about, running or not.
List() ([]Instance, error)
// Run boots a sandbox, detached.
Run(spec RunSpec) error
// PinsDigest reports whether Run honours RunSpec.Digest -- that is, whether
// this runtime's store is addressable by digest, so that booting
// Image@Digest boots that exact object and resolves it against the local
// store the way containerd does. brig only claims digest-level verification
// on a runtime that returns true; on one that returns false it verifies and
// boots the tag, and says so, rather than promise bytes it cannot pin.
PinsDigest() bool
// LocalDigest reports the digest the local store already holds for ref, or
// "" when it holds nothing under that reference or cannot say. It is how the
// verify path learns that the copy on disk is a different object from the
// one the registry serves. Only meaningful on a PinsDigest runtime; the
// caller does not ask the others.
LocalDigest(ref string) (string, error)
// Probe runs a command and reports only whether it succeeded, with all
// output discarded. Used for reachability checks.
Probe(spec ExecSpec) bool
// Output runs a command and returns its stdout.
Output(spec ExecSpec) (string, error)
// Feed runs a command with spec.Stdin on its standard input and discards
// its output, returning only whether it succeeded. The one method that
// carries a value into the guest off the command line.
Feed(spec ExecSpec) error
// Replace hands this process over to the runtime: the exec'd binary takes
// the terminal, the signals and the exit status. It does not return on
// success. This is how the agent's own TUI gets a real tty without brig
// sitting in the middle of it.
Replace(spec ExecSpec) error
// Attach runs the exec as a child of brig instead of replacing brig with
// it: brig's own stdin, stdout and stderr are inherited, brig waits, and the
// child's exit status is returned. A child killed by a signal is reported as
// 128 plus the signal number, the shell's convention.
//
// It is the counterpart to Replace for a caller that has to survive the exec
// to report on it -- which is what --json needs and Replace, having left
// nothing of brig behind, cannot do. Both build their command from the same
// function, so the terminal handover and the child cannot drift. See
// RunAttached and the note on Replace.
Attach(spec ExecSpec) (int, error)
// Stop stops a sandbox, and Remove clears the instance holding the name.
Stop(name string) error
Remove(name string) error
// Logs streams a sandbox's log to spec.Out, following until interrupted
// when spec.Follow is set. This reads a log; it does not own the sandbox, so
// an interrupt reaching the runtime leaves the sandbox running.
Logs(spec LogsSpec) error
// LogsHint is the command to suggest when a sandbox will not come up. It is
// kept beside Logs rather than replaced by it: a boot that never became a
// sandbox brig can drive still has a runtime command worth naming, and it is
// the second half of the advice `brig logs` leads.
LogsHint(name string) string
}
func Detect ¶
Detect picks a runtime for this host, with nothing asked of it.
The commands that work across every sandbox rather than one profile's -- listing them, removing them all -- use this: there is no profile in hand to take a preference from.
func DetectFor ¶
func DetectFor(pref Preference) (Runtime, error)
DetectFor picks a runtime, honouring what a profile asked for.
BRIG_RUNTIME forces the backend (hull | nerdctl) and BRIG_RUNTIME_BIN forces the executable, which is how you point brig at a build that is not on PATH. A profile's runtimeBin does the same thing without a variable per shell, and loses to the variable when both are set. Whichever named the binary, it is resolved and checked here, before any of it is run: a runtime that is not there is refused with the setting that named it, rather than reaching the first exec as a sandbox brig cannot tell the state of.
type Share ¶
type Share struct {
// write to. Only meaningful for directories that belong to the host: the
// workspace is the agent's own and stays writable.
ReadOnly bool
}
Share is a host directory made visible in the guest.
type SharedNetworkPruner ¶ added in v0.4.0
type SharedNetworkPruner interface {
// reports whether it did.
PruneSharedNetwork() bool
}
SharedNetworkPruner is a runtime with one network for the whole host, which it can stop once no sandbox is on it.
Apart from NetworkPruner because the question is different. inUse names sandboxes, and an isolated network has a record to match a name against. The shared network has no record of what is on it, and the list does not say which network a sandbox is on. The runtime decides from what it can see of the host, so this takes no list.
Optional for the same reason NetworkPruner is.
type Spinner ¶ added in v0.4.0
type Spinner interface {
// Spin shows what until stop is called, and stop clears it.
Spin(what string) (stop func())
}
Spinner is a notice writer that shows a long operation while it runs. The caller hands one in as RunSpec.Notice when the notice goes to a terminal.
type Var ¶
type Var struct {
Name string
Value string
// Secret marks a value brig resolved on the user's behalf from the store
// it owns, so that BRIG_ENV_ARGV never puts it in argv whatever it says:
// hull durably logs every exec's argv to a host file, and an ambient shell
// value put there deliberately is one thing, but a stored credential
// outliving the sandbox in a file the user never sees is a different
// severity of leak.
Secret bool
}
Var is one environment variable destined for the guest.
The value travels through the child process's own environment, never through argv: an implementation puts Name=Value in the environment of the runtime process it spawns and names only the bare Name on the command line. Values in argv would be readable in `ps` by any process on the host.
The exception is a name the runtime reads for itself, such as HOME. That value goes on the command line as Name=Value. See runtimeReads.
Source Files
¶
- attach.go
- boot.go
- bootdir_other.go
- bootfetch.go
- capability.go
- default_other.go
- doctor.go
- fallback.go
- gateway.go
- gatewayapi.go
- gatewayip.go
- gatewaypeer_linux.go
- hull.go
- isolation.go
- narrate.go
- nerdctl.go
- nerdctlegress.go
- netalloc.go
- networkgatewayinspect.go
- networkinspect.go
- posture.go
- publish.go
- published.go
- pullwatch.go
- runtime.go
- sandboxnet.go