Skip to content

Universal assistant architecture

The Universal Pohunek Assistant is designed as one capable coding-agent session, not as a separate runtime or a set of specialized agents. The launch command materializes a version-matched knowledge bundle, writes a redacted live snapshot, builds a short navigational prompt, and starts a normal PTY-backed session with initial input. Like every managed live session, that PTY belongs to its isolated pohunek-sessiond worker; restarting the public daemon reconnects to the same assistant runtime rather than relaunching it.

Knowledge delivery is pull-by-file. The prompt points the agent at this bundle, the snapshot file, and the source map; it does not inline the whole corpus. That keeps prompt size bounded and lets the same Markdown serve humans and agents.

The planned assistant command surface uses one implementation with intent filters: setup, project, update, debug, and help. Intent changes the initial table of contents and first-step steering, but the concepts and safety model are shared.

Local launches can bootstrap or verify the daemon before starting a session. Remote launches preserve the existing remote session safety model and require a knowledge bundle materialized on the host that runs the agent.

All shipped clients use the same public protocol v4. Each request advertises an inclusive minimum/maximum version range; the first response selects the highest overlap for that connection. A daemon accepts its own version and the previous one (3..=4 today) through shape-only adapters, while clients advertise only their own version. The old integer-v1 request envelope is not accepted. Waiting observation calls open dedicated connections so they do not block the caller’s ordinary control connection.

The shipped platform foundation centralizes target-neutral process identity, kernel Unix peer identity, and native service supervision contracts in crates/platform. Every process-identity consumer in the daemon and the worker resolves one host inspector from those contracts rather than parsing operating system records on its own. Linux uses the real procfs, pidfd, peer-credential, and systemd transient-unit backends; Darwin uses libproc process records, sysctl KERN_PROCARGS2 argument regions, and a kqueue EVFILT_PROC/NOTE_EXIT exit watch. Both backends read the same facts: process, parent, and process-group ids, an opaque same-boot start identity, the controlling-terminal foreground process group, the executable path, the working directory, and only the allowlisted POHUNEK ownership markers. A process that has exited but is not yet reaped keeps its identity on both backends and is reported as no longer running. Argument and environment regions stay separate, so an environment value is never argument evidence and an argument is never an ownership marker. An observation that fails is an explicit typed failure, never a healthy absence, and never authorizes a mutation. On Darwin a live process between images can expose an argument region that is absent, uncopyable, or not yet laid out; that process is still listed, without a command line, and its ownership markers read as unobservable rather than unmarked, so the runtime sweep skips it as unreadable and never signals it while the rest of the process table is still classified. Darwin also reports the kernel’s boot-wide creation number of every same-user process and of the process that created it. The recorded creator keeps its identity after it exits, until the process re-executes after being reparented, when the kernel rewrites it to launchd’s number, so a launchd creator proves nothing. When the sweep knows the lost worker’s creation number, that lineage decides a process whose environment the kernel withholds: a descendant of the worker is reaped, a process created before the worker or under an older non-launchd creator is ignored, and every other one (a re-executed orphan, creator gone) stays skipped as unreadable. When the sweep also knows the worker’s coalitions, a skipped process that the kernel puts in a resource and a jetsam coalition that both differ from the worker’s, such as a job launchd started after the worker, is ignored as well: a descendant keeps the worker’s coalitions through every fork, exec and reparenting.

Darwin adds a privilege boundary Linux does not have: the kernel serves most process records only to the owner of the target process and refuses everyone else, so ownership is settled first through the short process record, the one record it serves for every process id. A process owned by another user is therefore outside the same-user contract and reported as absent, while a privileged fact refused for a process the caller does own stays a denial. Three facts need a privilege pohunek does not ask for and so are unavailable on macOS: the working directory of another user’s process, that process’s argument vector, and the environment of a code-signing-restricted process, whose environment region the kernel omits even for the owner. A process whose environment cannot be read reports no ownership markers, which keeps it observable rather than adoptable. An argument vector is an optional fact for the same reason: a process that has not finished its exec, or whose address space is being replaced or torn down while the kernel copies it out, is reported without a command line rather than failing the inventory it appears in. The shared secure-path contract preserves XDG config/data/state/cache precedence and adds a short, owner-private macOS runtime default without moving durable host identity. Native Apple Silicon CI compiles and tests these shared contracts with a macOS 14 deployment target, but this does not mean complete macOS host or client support is available. Intel Macs are outside the current release scope. Darwin kernel peer identity and the session worker’s portable PTY readiness are in place: the worker waits for PTY output with one poll(2) implementation shared by both targets, and its complete test suite runs natively. The daemon and CLI build and test natively on macOS too. Client integration, signed artifacts, and the remaining native acceptance stay explicitly deferred through issues #101-#105 under the Complete macOS support milestone and macOS Project. The issue hierarchy, milestone, and Project own delivery scope, sequencing, and status; the accepted macOS RFC records design constraints rather than live tracking state.

Session workers are native service jobs, one per worker generation, supervised by the same lifecycle engine on both targets: systemd transient units (pohunek-<ns>-worker-<session-id>-<generation>.service in pohunek-<ns>-sessions.slice) on Linux, and launchd jobs (io.github.zajca.pohunek.<ns>.worker.<session-id>.<generation>) whose definitions stay in a private state directory on macOS. The daemon is the login service of pohunek itself (pohunek service install); the only other login units are the jobs and services of enabled workflow plugins (pohunek-<ns>-wf-* units, io.github.zajca.pohunek.<ns>.wf.* agents), which run the CLI’s pohunek workflow __exec trampoline, never the daemon, and which worker discovery never adopts (see the workflow plugins guide). <ns> is derived from the user ID and the canonical state and runtime roots, so separate installations never adopt or retire each other’s jobs. Workers live for the login session: closing a terminal or locking the screen is safe, while logout or reboot ends them and the next login reports those sessions lost without restarting them.

Owner paths and the accepted relay direction

Section titled “Owner paths and the accepted relay direction”

Current protocol-v4 operation is owner-only. Local clients connect to the Unix socket, direct remote clients use a configured overlay such as NetBird, and external clients such as a browser control center can map one WebSocket to one daemon connection through their own relay. Each host daemon remains authoritative for its sessions and each worker remains authoritative for one live PTY. The owner path remains supported after the team relay ships.

Pohunek core ships no user interface. GUIs, the web control center, and desktop launchers are external clients developed in zajca/pohunek-work. They use public contracts only: the CLI with --json, or the public protocol through the Rust crates (pinned by git tag) and the TypeScript SDK (release tarballs pinned by URL and integrity). They pin the protocol version they were built against (a daemon also serves the previous version), and the core crates they link are a pinned, not a stable, API. Issue and PR providers live only in zajca/pohunek-work.

Pohunek has an optional team-relay design with an implemented reduced foundation. The PostgreSQL-backed relay provides fencing, recovery, protected initial provisioning, generic OIDC browser/device authentication, and bounded HTTPS account and credential lifecycle. Standalone and direct NetBird modes remain independent. Host links, HostShare, session origin, routing, attach, team administration, and the team WebUI remain deferred. The relay has no local mode; its future team WebUI and any owner-path browser client use separate explicit API adapters, credentials, state, and origins.

Protocol v4 and the typed host/team API will arrive only through their linked implementation issues. The current bounded foundation API and native relay CLI do not imply a host or team surface. Assistants must not infer future commands or fields from the RFC; verify currently available behavior in docs/public-api.md through the source map.

The planned relay contract is normative only in the RFC sections on identity, recovery, scheduling, resources, snapshots, catalog, audit, and dependencies. Its implementation path is completed reduced #85 → #107/#108/#92 → complete #72 → #70/#82/#83/#84/#71 → #86 and #87, then post-release #73/#88. The first relay release trusts collaborators at the host Unix-account boundary; its ACLs do not provide workload isolation.

Related concepts: