paratenic

Plate VII · Operate

Run it deliberately.

Paratenic starts private: loopback only, owner-only state, a socket only you can reach. Everything else — remote clients, a provider that starts Pi for sessions clients create, services that start at boot — is a choice you make explicitly.

Install

Two binaries and a Pi package

nix develop .#web        # pinned native, WASM and Node build tools
just install all         # ~/.local/bin/paratenic and culture; INSTALL_DIR to override
nix build .#paratenic    # or the Nix package; the extension is in share/paratenic/extensions

The Pi extension is the repository’s paratenic-pi package: load extensions/paratenic.mjs into the Pi you run. Release archives ship paratenic, culture and, on Apple Silicon, Culture.app.

Run

Host, sessions, catalogue, client

paratenic serve                          # 127.0.0.1:7788 · ~/.local/state/paratenic
pi -e ./extensions/paratenic.mjs         # attach the Pi you are using
paratenic provider pi                    # optional: Pi in tmux for createSession
culture                                  # or: culture web
A typical deployment Two nodes, atlas and vega, each run Paratenic as a user service listening only on loopback. On atlas the Pi provider starts Pi in tmux for sessions clients create, and terminal Pi sessions register over the backend socket; on vega terminal Pi sessions do. Each node is reached through an authenticated tunnel or a TLS proxy the owner controls. Culture on a laptop and on a phone and watch connects to both nodes independently over wss with a bearer token, and to pi-daemon or any other AHP host the same way. paratenic serve127.0.0.1:7788 · user service provider pi Pi in tmux Pi in a terminal tunnel or TLS proxyyours paratenic serve127.0.0.1:7788 · user service Pi in a terminal tunnel or TLS proxyyours Cultureterminal · web · macOS Culturephone · watch NODE · ATLAS NODE · VEGA loopback loopback wss · bearer
Fig. VII.1Hosts stay on loopback. Reach them through a tunnel or proxy you already trust; Culture connects to each one separately.

Services

The same options on NixOS, macOS and Android

{
  imports = [ inputs.paratenic.nixosModules.paratenic ];  # or darwinModules / nixOnDroidModules
  services.paratenic = {
    enable = true;
    bind = "127.0.0.1:7788";
    piProvider = { enable = true; piArgs = [ "--no-skills" ]; };
  };
}
OptionMeaning
bind127.0.0.1:7788 unless you choose otherwise
stateDir, backendSocketowner-private state; the socket defaults to inside it
tokenFilean existing owner-only file with one bearer token line
allowPlaintextNonLoopbackyour statement that a TLS proxy you own protects the listener
preferLocalBinaryuse an owner-local paratenic update, keeping the Nix package as fallback
piProvider.*run paratenic provider pi as a service: enable, named profiles (each an agent with its own piArgs, cwd, model, thinking), or a single providerId with piArgs; allowedRoots for working directories; and extra arguments
extraArgsappended to serve

The modules run a systemd user service, a per-user launchd agent or a Nix-on-Droid supervisord program, each with restart policy and an owner-private umask. services.culture.web and services.petri are separate, opt-in services.

Authentication

A token in a file, never on a command line

install -m 600 /dev/null ~/.config/paratenic/token
openssl rand -hex 32 > ~/.config/paratenic/token
paratenic serve --bind 0.0.0.0:7788 \
  --token-file ~/.config/paratenic/token \
  --allow-plaintext-non-loopback          # only behind a TLS proxy you control
  • Where it applies. Clients send Authorization: Bearer … on the WebSocket upgrade and on /sessions and /providers. /healthz and /status stay content-free.
  • Managed secrets. SOPS and other secret symlinks work; permissions are checked on the resolved file.
  • Clients. Culture reads token_file per host; the CLI tools take --token-file and never put credentials in a URL.
Bearer over plaintext is not a secure remote deployment.

Terminate TLS in front of the listener, or keep it on loopback and reach it through an authenticated encrypted tunnel.

Inspect

Ask the host, not its files

CommandAnswers
paratenic statussessions connected and total, clients, uptime, accepts and disconnects, authentication failures, backends, the server sequence; reachable: false after two seconds
paratenic lseach live session: stable id, source and instance, provider, lifecycle, online
paratenic pslocal serve processes from their receipts: PID, port, start time, state directory
/providers?provider=piprovider readiness: connection id, receipt age, completeness
paratenic log tail -fthe owner-only structured log; --remote atlas runs the same command over SSH
paratenic harness pia real Pi, isolated, registering against an ephemeral host — never your sessions

Every command has --json with stable camel-case fields and accepts --url and --token-file for a remote host.

Update

Replacing a binary is not a deployment

paratenic update --check
paratenic update                         # --restart-policy auto | none | systemd --restart-unit NAME
culture update                           # on Apple Silicon also replaces ~/Applications/Culture.app

Updates are verified releases installed owner-locally. On Linux the auto policy asks exactly one active user unit to try-restart and reports ambiguity instead of guessing; it never starts a stopped service. The result separates promoted — the binary was replaced — from anything about the running process, and always says deploymentVerified: false: check paratenic status before calling a rollout done.

Troubleshoot

When something looks wrong

You seeLook at
a terminal Pi never appearsrun /paratenic inside Pi; both sides use $XDG_STATE_HOME/paratenic/backend.sock unless PARATENIC_BACKEND_SOCKET or PARATENIC_STATE_DIR says otherwise
“Send unavailable while Pi is busy”expected on an attached session; the draft is kept for when Pi is idle
a command is indeterminatethe response was lost after delivery; inspect the session before sending again — nothing retries for you
a stale process gets no commandsexpected: a newer registration for the same source fenced it
created sessions never start/providers?provider=pi readiness; that tmux and pi are on the provider’s PATH; tmux ls for the agent’s paratenic-… session
a remote upgrade is refusedTLS in front, the token file’s mode and contents, and the client’s Bearer header

Develop

The gate before a change

nix develop
just check                               # fmt, Clippy -D warnings, every Rust target, Node extension tests
python3 scripts/site/validate.py         # this site

Pull requests run a fast Linux source lane; tagged releases and manual full runs build and test the Linux and Darwin package graphs and every browser. Keep the backend protocol runtime-neutral, keep the terminal usable when the host is gone, and cover changed behaviour with focused Rust or Node tests.