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
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" ]; };
};
}
| Option | Meaning |
|---|---|
bind | 127.0.0.1:7788 unless you choose otherwise |
stateDir, backendSocket | owner-private state; the socket defaults to inside it |
tokenFile | an existing owner-only file with one bearer token line |
allowPlaintextNonLoopback | your statement that a TLS proxy you own protects the listener |
preferLocalBinary | use 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 |
extraArgs | appended 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/sessionsand/providers./healthzand/statusstay content-free. - Managed secrets. SOPS and other secret symlinks work; permissions are checked on the resolved file.
- Clients. Culture reads
token_fileper host; the CLI tools take--token-fileand never put credentials in a URL.
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
| Command | Answers |
|---|---|
paratenic status | sessions connected and total, clients, uptime, accepts and disconnects, authentication failures, backends, the server sequence; reachable: false after two seconds |
paratenic ls | each live session: stable id, source and instance, provider, lifecycle, online |
paratenic ps | local serve processes from their receipts: PID, port, start time, state directory |
/providers?provider=pi | provider readiness: connection id, receipt age, completeness |
paratenic log tail -f | the owner-only structured log; --remote atlas runs the same command over SSH |
paratenic harness pi | a 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 see | Look at |
|---|---|
| a terminal Pi never appears | run /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 indeterminate | the response was lost after delivery; inspect the session before sending again — nothing retries for you |
| a stale process gets no commands | expected: 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 refused | TLS 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.