embedded

package
v0.2.3 Latest Latest
Warning

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

Go to latest
Published: Sep 25, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package embedded is the shared, build-tag-free lifecycle core for embedding the `any` HTTP server inside a host process. Both mobile binding shims sit on top of it: the gomobile `mobile` package (`mobile/android`) and the c-archive shim (`mobile/ios`). Each shim is a thin host-idiom adapter; this package owns the real logic so it is exercised once by a single host-runnable test suite.

What it owns:

  • a mutex-guarded, single-server-per-process singleton with a double-start guard;
  • a drain (`stopping`) channel so a fast restart waits for a prior hard-stop's teardown before re-booting on the same data dir (otherwise a background->foreground restart could run two engines against one data dir);
  • the GOMEMLIMIT soft cap via debug.SetMemoryLimit (iOS jetsam AND Android's low-memory killer);
  • config assembly: config.Defaults() + DataDir + Listen.Addr + Network.Nodeconf + index policy (Embedder="none": FTS only) + headless (WebUI.Enabled=false) + the push node peer (Options.PushPeerId/PushAddrs → cfg.Push);
  • the run via main's canonical embedder seam, server.RunWith.

The embedded path never reads config.yaml or ANY_* env — the host owns every input and passes it explicitly through Options. Env in a mobile app process is not a configuration channel.

It installs NO os/signal handlers — the caller owns the lifecycle.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrAlreadyRunning: a server is already started in this process. The
	// caller must Stop the current one before starting again.
	ErrAlreadyRunning = errors.New("embedded: server already running")
	// ErrBadDataDir: the data directory argument is empty, or could not be
	// resolved / created (bad path, unwritable parent).
	ErrBadDataDir = errors.New("embedded: bad data directory")
	// ErrBadOptions: an Options field the host controls is invalid — an
	// unknown Mode, managed mode without a ControlToken, or a NodeconfYAML
	// that isn't YAML or names no networkId.
	ErrBadOptions = errors.New("embedded: bad options")
)

Exported sentinel errors. Both shims map from this one source so the host error contract has a single definition. The iOS c-archive shim (mobile/ios) maps each to a C code the Swift side mirrors:

ErrAlreadyRunning               -> 1
ErrBadDataDir                   -> 2
a boot failure                  -> 3 (see BootError)
indexer.ErrIndexRebuildRequired -> 4 (inside a BootError; checked first)
ErrBadOptions                   -> 5 (a host programming error, never retryable)

The Android gomobile shim (mobile/android) surfaces the error string and no code today; converging it is Android-owned work.

An empty nodeconfYAML is not an error: it selects the embedded production nodeconf, the same default the CLI and desktop sidecar boot on. Hosts pass YAML only to override the network.

Functions

func Address

func Address() string

Address returns the actually-bound listen address (host:port). Differs from the listenAddr passed to Start when ":0" was used and the OS picked a port. Empty when the server is not running.

func Start

func Start(opts Options) (string, error)

Start boots the embedded server with its data under opts.DataDir, listening on opts.ListenAddr, joining the network described by opts.NodeconfYAML (see Options for the full input contract). It blocks until the listener binds — returning the bound address — or boot fails, returning one of the package's typed errors (ErrAlreadyRunning, ErrBadDataDir or a *BootError).

func Stop

func Stop(graceful bool)

Stop tears down the running server. When graceful, it cancels the run context and waits for RunWith to return (Echo's graceful drain); when not graceful, it cancels the context and returns without waiting for a clean drain so the hard-stop path stays bounded for the iOS background- task expiration handler. Either way the handle is cleared so a subsequent Start is clean.

The non-graceful path returns while the run goroutine is still draining, so it records that teardown in `stopping`; the next Start waits on it before booting to avoid running two engines against one data dir. Safe to call when no server is running.

func StopNow

func StopNow()

StopNow hard-stops the server and returns promptly (well under 1s): it cancels the run context to force the listener closed but does NOT wait for a clean drain. The iOS beginBackgroundTask expiration handler calls this synchronously, so it must not block on shutdown.

func Version

func Version() string

Version returns the build-stamped version string ("any <version> (commit <sha>, built <ts>)").

Types

type BootError

type BootError struct{ Err error }

BootError wraps a failure that occurred while bringing the server up after the config validated — engine/account boot, listener bind, or a config the runtime rejected. The iOS shim maps a BootError to code 3, EXCEPT when it wraps indexer.ErrIndexRebuildRequired, which gets its own code 4 because the host has a specific recovery to offer. The distinct type lets a caller tell "the config was fine but boot failed" from "the input was bad" (ErrBadDataDir) without string matching.

func (*BootError) Error

func (e *BootError) Error() string

func (*BootError) Unwrap

func (e *BootError) Unwrap() error

type Options

type Options struct {
	// DataDir is the server's data root (Context.getFilesDir() on
	// Android, the app container on iOS). Required.
	DataDir string
	// ListenAddr is the loopback listen address; "127.0.0.1:0" lets the
	// OS pick a free port (read it back with Address()).
	ListenAddr string
	// NodeconfYAML is the any-sync network config contents (staging/prod
	// yml). Required — there is no filesystem fallback on this path.
	NodeconfYAML string
	// PushPeerId is the push node's peer id. The push node is a
	// direct out-of-band peer, deliberately NOT part of NodeconfYAML —
	// but it pairs with the nodeconf choice (staging vs prod), so the
	// host supplies both from the same place. Empty = push stays off
	// (every /v1/push endpoint returns 409 push.disabled).
	PushPeerId string
	// PushAddrs are the push node's dial addresses, comma-separated —
	// the same format ANY_PUSH_ADDRS parses, e.g.
	// "quic://host:port" or "host:port,host2:port2". Push activates only
	// when both PushPeerId and PushAddrs are non-empty.
	PushAddrs string
	// GlobalP2PEnabled overrides the internet-wide p2p layer: an iroh
	// UDP endpoint, a permanent relay session, and a dialable ticket
	// published into the records of every space this account holds.
	// nil takes the same default the CLI gets — on when the host
	// supplied no NodeconfYAML, i.e. the production network. A host
	// that does not want the endpoint sets this to false; there is no
	// config file or env on this path, so this struct is the only way
	// to say so. Publishing a ticket is one-way (docs/30-global-p2p.md
	// § Advertising is a one-way door), so a host that is unsure
	// should decide before the first boot, not after.
	GlobalP2PEnabled *bool

	// Mode is the server ownership mode: "" / "standalone" (the account
	// resolves from the wallet on disk) or "managed" (the host states
	// the account over POST /v1/auth on every boot, keys never touch
	// disk, and logout / switch / shutdown are allowed behind
	// ControlToken). Fixed for the server's lifetime.
	Mode string
	// LocalDiscovery is p2p.localDiscovery: whether mDNS announce and
	// browse start with the engine. nil resolves per platform and mode
	// (config.LocalDiscoveryEnabled). An embedded host that owns a
	// Local Network permission flow passes false here rather than over
	// HTTP: a standalone server with an account on disk boots before the
	// listener is up, so a PUT /v1/local-discovery could not precede it.
	LocalDiscovery *bool
	// ControlToken gates the managed control operations (POST/DELETE
	// /v1/auth, POST /v1/shutdown, PUT /v1/local-discovery). Required
	// when Mode is "managed":
	// an in-process host has no stdout handshake to receive a minted
	// one, and without a token any other process on the device could
	// switch the account. Ignored in standalone.
	ControlToken string
}

Options are the host-supplied inputs for Start. The embedded path has no config file and no env — every knob a host can turn crosses this struct explicitly.

Jump to

Keyboard shortcuts

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