engineering

When claude -p becomes part of your backend

Rheo runs Claude Code from a webhook service, showing why process lifecycle, durable state, tool access, and credential recovery become backend concerns.

By Dee Fault · Sep 2026 · 8 min read

Anthropic documents claude -p as a non-interactive query that exits. On its own, it is a small interface: give the command a prompt, collect its output, and move on.

Rheo uses that interface from a webhook service. A message arrives, the service gathers context and builds a prompt, then starts a separate claude -p process. The webhook returns before Claude has finished. The service sends the answer later.

That changes the problem. The command is short-lived, but the application around it has work to preserve, capabilities to account for, and failures to recover from. claude -p is still a CLI command. Once an application depends on it, it is one layer of the backend.

Rheo is one implementation, not a reference architecture. It makes the seams visible: Anthropic supplies non-interactive execution, while Rheo decides what happens before the process starts, while it runs, after it exits, and when it cannot authenticate.

The service owns the lifecycle around the command

The Claude Code programmatic usage documentation describes -p, or --print, as the way to run Claude Code non-interactively. The CLI reference puts it more simply: -p runs a query and exits.

Neither statement defines the lifecycle of the application that launches the query.

In Rheo, the service assembles conversation context, supplies a task prompt and system prompt, starts a new child-process session, and reads the result. It passes a fixed list through --allowedTools and sets a time limit. The webhook handler does not hold the incoming request open while this happens. It returns. A background handler waits for the child process and sends the answer when it is ready.

Those details decide who owns a partially completed request and where a failure appears. If prompt construction fails, the service has failed. If the process reaches its time limit, the service has to deal with it. If the webhook already returned, the answer needs another route back to the user.

It is easy to label the CLI synchronous or asynchronous and leave it there. That misses the useful question. One part of an application can wait for a process while the application stays responsive because another part owns that wait. Who starts the process? Who watches it? What ends it? Who carries the answer after the original request has closed?

This code is part of Rheo's agent harness, the execution scaffold that provides context, permissions, and control flow around the model. The command handles the model-facing work. The harness makes it application behavior.

A fresh invocation can continue durable work

A claude -p process can exit before the larger job is finished. Rheo uses that for longer framework work.

When a run reaches a checkpoint, it writes durable state and appends to a run log. The worker then exits. A later message starts a fresh claude -p process against the recorded work. The durable unit is the run, not the CLI session that happened to advance it.

That distinction can blur because Claude Code also has conversation continuation flags. Anthropic documents -r and --resume as ways to resume a Claude Code session. Rheo's run state solves a different problem: it records what the application is doing, the checkpoint it reached, and what should happen next. A new Claude process can continue the application-level run after the old process has exited.

The difference shows up when a second request arrives. Rheo writes its active-run record atomically. Before starting more work, the resume path checks whether the recorded process is still alive. If it is, the service refuses the duplicate launch rather than letting two workers advance the same run.

That is neither a claude -p feature nor a rule for every integration. It is Rheo's answer to a service-boundary problem: messages can arrive independently, while one worker should own the active run. Another application might queue requests or make its operations idempotent. It still has to decide what a duplicate means.

An invocation can be disposable. The work cannot be, unless the application is willing to discard it. State has to cross the process boundary somewhere.

Capabilities have to be deliberate

Rheo passes a fixed list through --allowedTools when it starts general Claude calls. Its service documentation says that a new MCP tool must also be added to that list before the worker can use it. That local maintenance rule matters, but the flag's documented meaning is narrower than a complete tool boundary.

Anthropic describes --allowedTools as a way to let named tools execute without prompting. It controls automatic approval. It does not by itself establish that every other configured tool is unavailable. The CLI documents --tools separately as the control for which tools are available. Approval and availability are related, but they are different settings.

An MCP server can expose tools to Claude Code. The process can have separate rules for which tools are available and which can run without a permission prompt. A backend integration has to account for both. Calling the --allowedTools list a complete capability boundary would hide the distinction.

Anthropic's programmatic usage page also documents --bare. Bare mode skips automatic discovery of hooks, skills, plugins, MCP servers, auto memory, and CLAUDE.md. Anything a scripted run needs must then be configured explicitly.

Rheo does not use bare mode, so it would be wrong to present it as part of this implementation. But it is a useful design test. If automatic discovery disappeared, could you name every capability the backend process depends on? A scripted process has a dependency list whether the application writes it down or inherits it from its environment.

The application needs to account for discovery, availability, and approval when it constructs the command. The policy may differ by invocation. But if a worker needs a capability, the surrounding service should know where it comes from and what permits it to run.

Authentication failure changes the operating model

The sharpest lesson in Rheo came from authentication, not process control.

The services that invoked claude -p on a headless machine used an interactive OAuth session. At times the session expired and its refresh path failed. Claude-calling work stopped until an operator reauthenticated.

This is a bounded first-party incident. It does not show that every OAuth-backed Claude Code deployment fails this way, and it says nothing about general availability. It shows what an authentication failure means when a CLI becomes application-critical. A credential that once interrupted a person's terminal session can now stop a service with nobody at that terminal.

Rheo added a small recovery component around that failure. It guides an operator through reauthentication, keeps the resulting token masked in the interface and redacted from UI state, writes the credential to shared configuration with restricted permissions, and restarts the configured Claude-using services so they load the replacement.

The important part is not the interface or the particular credential flow. It is ownership. The backend needs a visible failure, a human handoff where the provider requires one, protected credential handling, and a way for dependent processes to recover afterward. Reauthentication alone is incomplete if running services keep using stale configuration.

There is one adjacent vendor fact worth keeping narrow. Anthropic's environment variable reference says that ANTHROPIC_API_KEY, when present, is used for non-interactive mode. That describes a supported configuration boundary. It is not a recommendation to replace OAuth, and the Rheo incident does not establish a general comparison between the two approaches.

The incident removed a comforting assumption: successful authentication at process startup is not a permanent property of the backend. Credentials expire, refreshes can fail, and some recovery steps still need a person. The application does not have to eliminate those facts. It has to expose them and provide a safe way back to a working state.

The available evidence documents this failure and Rheo's recovery design. It does not establish broader results about availability, request volume, performance, or cost.

The CLI is one layer of the backend

claude -p does what its documentation says. It runs a query non-interactively and exits. The complications in Rheo come from depending on that behavior inside a longer-lived system.

Rheo's service owns the webhook and eventual reply, while durable run state lets new processes continue old work. It also has to account separately for tool discovery, availability, and approval. The authentication recovery path turns an expired session into an operator action and a controlled restart.

None of these choices is universal. A batch job, a local utility, and a webhook service will draw their boundaries differently. The responsibility does not disappear, though. Once application behavior depends on a CLI process, process lifetime, state handoff, capabilities, and credential recovery are backend design.