Instance Admin Guide

Instance Admin Guide

Backend configuration, runtime architecture, repository synchronization, and live status updates for Pointy instance administrators.

Pointy stores workflow definitions in a Git-backed Nix flake that the browser and backend edit together. Git records every change; Nix evaluates and sandboxes the result; Slurm schedules the jobs. Administrators keep those layers connected so the repository remains the source of truth.

If you are looking for everyday workflow tasks in the web UI, start with Managing Projects, Building Workflows (Steps), and Execution and Data Management.

Runtime architecture

A Pointy deployment has five moving parts:

  1. Frontend: the browser UI where users manage projects, steps, runs, outputs, and share links.
  2. Backend: a service that reads and writes the user repository, serves API endpoints, streams live step statuses, accepts uploads, and starts or stops builds.
  3. Backend-owned Nix REPL child: the backend keeps a persistent nix repl child process for nix eval requests, reuses loaded flake bindings across requests, and restarts the child if it crashes.
  4. User repository: a Git-backed Nix flake that stores templates, optional presets, step definitions, project definitions, and source files.
  5. Nix / Slurm runtime: builds are submitted as Slurm batch jobs that call nix build against a pinned commit of the user repository.

When a user edits a project or step, the backend rewrites the corresponding .nix file, commits the change, and pushes it to the configured remote. Running a step evaluates that definition from a specific commit and broadcasts status changes to every project containing the step. Editing and execution are connected by an explicit repository revision, not by mutable in-memory state.

Backend configuration

The backend reads /home/backend/config.toml by default; set POINTY_CONFIG_PATH when the deployment keeps this instance-specific configuration elsewhere.

The current configuration format is:

[user-repo]
url = "git@example.com:org/user-repo.git"
keyfile = "/path/to/deploy-key"
branch = "main"

[slurm]
# Optional. Empty fields defer to Slurm defaults.
partition = ""
account = ""
time-limit = ""
extra = []
# "scheduler" passes --mem and --cpus-per-task; "metadata-only" records requirements but does not enforce them.
enforcement = "scheduler"
[agent]
# The backend invokes runner-command through sbox-command from the session worktree.
sbox-command = "sbox"
sbox-args = ["--bind", "{home}", "{home}"]
runner-command = "pi"
runner-args = ["-p", "-c", "{prompt}"]
timeout-seconds = 1800
output-limit-bytes = 1048576
# Parsed for configuration compatibility; automatic age-based pruning is not currently implemented.
session-retention-days = 7
# Omit this field to use the built-in repository-context bootstrap prompt.
# Set it to an empty string to disable warm bootstrapping.
bootstrap-prompt = "Read AGENTS.md and skill://pointy-router. Do not modify any files. Reply READY when you understand the keyword skill map for non-technical user requests."

These settings tell the backend:

  • which Git remote to use for the user repository
  • which SSH key to use for Git operations
  • which branch contains the live Pointy state

The optional [slurm] section controls how the backend submits build jobs:

  • partition: selects a Slurm partition when non-empty.
  • account: passed as sbatch --account when non-empty.
  • time-limit: passed as sbatch --time when non-empty.
  • extra: appends raw sbatch flags for site-specific policy.
  • enforcement: sets the Slurm resource-enforcement mode. "scheduler" (the default) passes --mem and --cpus-per-task to sbatch. "metadata-only" records the values in the job comment but does not enforce them at the scheduler level.

The [agent] section configures the embedded LLM agent. When this section is absent, the backend uses the defaults shown above. Runner secrets are provisioned through the NixOS module, not through config.toml; see NixOS module options. For the user-facing agent workflow, see the AI Agent guide.

NixOS module options

Import pointy.nixosModules.pointy-host to install the backend service, single-node Slurm runtime, nginx virtual host, and agent sandbox integration. The host module exposes:

OptionDefaultPurpose
services.pointy-host.hostname(required)Public nginx hostname; ACME and HTTPS are enabled for this virtual host.
services.pointy-host.frontendPackage(required)Built frontend package served at /.
services.pointy-host.basicAuthFilenullOptional htpasswd file protecting the application virtual host.
services.pointy-host.docsPackagenullOptional documentation package served at /docs/; this location disables the application's basic-auth gate.
services.pointy-backend.agentEnvFilenullRuntime KEY=VALUE secret file sourced by systemd and exposed to the curated agent environment, suitable for a sops-nix or agenix path.
services.pointy-backend.piConfigDirPointy's backend/piPi configuration copied to /home/backend/.pi before the backend starts.

The nginx configuration keeps /backend/ SSE responses unbuffered and allows hour-long reads. If you replace the supplied reverse proxy, preserve those settings for status and agent-turn streams.

What the user repository must contain

You own the flake skeleton and templates; the backend owns steps and projects. This split keeps your authored content cleanly separated from backend-generated files. You can evolve templates without the backend's commits getting in your way.

At minimum, the user repository needs:

  • flake.nix
  • flake.lock
  • templates/
  • optional presets/
  • steps/
  • projects/
  • srcFiles/

templates/, optional presets/, and srcFiles/ are yours to author. steps/ and projects/ are backend-managed. flake.nix wires everything together through pointy-stdlib.lib.mkFlake.

You can keep additional repo content such as helper Nix files or a packages/ directory, but that layout is your own convention. Pointy won't discover it automatically.

For the concrete flake setup, see Setting Up the User Repository.

Project definitions must choose either a preset or a custom template list. If templates, presets, or step ids disappear from the user repository, Pointy keeps the project loadable where possible and surfaces validation errors in the UI.

Repository synchronization

The backend commits and pushes on every user edit, and it fetches to stay current with the remote. You don't normally need to touch the repository. When histories diverge, for example because someone pushed directly to the remote, the backend resolves conflicts automatically:

On fetch

When a fetch is rejected as non-fast-forward, the backend:

  1. tries to push any unpublished local commits
  2. retries the fetch if that push succeeds
  3. falls back to a force-fetch if the push also fails

This guarantees convergence on a definite state even when local and remote histories conflict.

On push

When a push is rejected because the remote moved ahead, the backend runs git pull --rebase and pushes again.

Live status updates

The frontend listens for live step-status snapshots on:

  • /backend/step-status-stream?project_id=<id>
  • optionally &commit=<hash> for pinned read-only views

This is a Server-Sent Events (SSE) endpoint. Pointy sends:

  • an initial snapshot for the requested project
  • periodic heartbeat events
  • replacement snapshots when project status changes

When the stream is opened without commit, it follows the live head of the configured user-repository branch. When commit=<hash> is present, the stream is pinned to that historical read-only view and only emits snapshots for that commit.

If you place Pointy behind a reverse proxy, make sure this endpoint is allowed to stay open for a long time and that response buffering is disabled.

Nix evaluation child

nix eval requests use a backend-owned persistent nix repl child whose loaded flake bindings remain warm across requests. If that child exits or stops responding, the backend discards it, starts a replacement, and retries the request once.

Agent runner configuration

The embedded agent runner is configured through the [agent] table in config.toml. Every field is optional; the backend falls back to the defaults shown in the configuration reference above.

Sandbox and credential boundaries

The agent process runs inside sbox (configurable via sbox-command and sbox-args), with the session worktree as its working directory. By default, the per-session {home} directory is bind-mounted into the sandbox, while the user-repository Git directory, Pi agent configuration, and optional warm-session file are mounted read-only. The backend's own HOME, SSH keys, and Git credentials are not passed to the runner.

The runner environment is built from a curated allowlist: USER, LOGNAME, SHELL, TERM, locale and time-zone variables, XDG runtime/data paths, and the supported LLM provider API keys. Other backend environment variables are stripped. Supply provider secrets through services.pointy-backend.agentEnvFile rather than config.toml.

Placeholder expansion

For normal agent turns, placeholders in both sbox-args and runner-args are expanded before the process starts:

  • {worktree}: absolute path to the session worktree (the sandbox CWD)
  • {home}: per-session HOME directory ({worktree}/../home), created before the runner starts
  • {sessionRoot}: {worktree}/.., the session root directory
  • {sessionId}: short opaque session identifier
  • {prompt}: the user-supplied prompt text

Warm bootstrap

When bootstrap-prompt is non-empty, the backend keeps one warm Pi session for the current repository base commit and configured prompt, rebuilding it when either changes. On the first turn of a new chat, the backend forks that warm session so the agent starts with project context already loaded. Subsequent turns continue the chat's own conversation. Set bootstrap-prompt to an empty string to disable warm bootstrapping and start chats cold.

Time, output, and session storage

  • timeout-seconds (default 1800): wall-clock limit for a single agent turn. The runner process is terminated if it exceeds this limit.
  • output-limit-bytes (default 1048576, 1 MiB): per-turn log size cap. Once the log reaches this limit, further output lines are discarded.
  • session-retention-days (default 7): accepted by the current configuration schema but not yet used for automatic pruning. Users remove stored sessions with Permanent delete after archiving them.

NixOS integration

Use services.pointy-backend.agentEnvFile for provider API keys and services.pointy-backend.piConfigDir for model and tool configuration; both options are described under NixOS module options.

Build execution and GC roots

Each build runs as a Slurm batch job whose job name is derived from the step output path. The default NixOS module configures a single-node Slurm runtime. Production admins can override Slurm settings for remote workers, provided those workers have access to the same Nix store and user repository.

Running a step expands its complete transitive step-reference graph. Pointy submits one Slurm job for each unbuilt step in dependency order. It reuses outputs already in the Nix store and jobs already in flight, and connects dependent jobs with afterok edges. A dependent is not submitted when one of its prerequisites cannot be evaluated or scheduled, so a step never runs with incomplete upstream results.

When a build succeeds, the backend registers a GC root under the backend user's home directory, for example:

  • /home/backend/.local/state/pointy/gc-roots/

This keeps successful step outputs alive across nix-collect-garbage. To allow a specific output to be collected, remove the matching GC-root entry first.