Penv Cloud
Start free
Docs
Conceptsince cloud@2026-09-10

Coding agents

penv detects coding agent sessions, seals and masks your values, and never prints a value to one without your approval. It cannot stop a process running as you.

An agent runs as you and can read anything you can read. penv treats its session as a separate actor: values are sealed and masked, and a reveal needs your approval. Setup and the full reveal flow: keep agents out of .env.

Threat model

penv stopspenv does not stop
The agent reading .env or .env.* through a harness that enforces the rules penv guard writesA process running as you reading values directly
A @hosts key reaching the agent's command as anything but a placeholder (sealed runs)A sealed command calling any endpoint of an allowed host that the key permits
Values of four or more characters appearing in child output captured by the agent (penv run masks them)A harness that does not enforce a rule penv wrote
A value printing without your approval (penv reveal exits 4, or the hook refuses it)Anything inside the child process that received an unsealed value
A .env written to disk from an agent session (penv pull exits 2)

Agent detection

Detection is a ladder tried in order, because vendors set colliding variables. The first rung that matches wins.

OrderSignal
1A vendor's own marker, such as CLAUDECODE, CODEX_THREAD_ID or CURSOR_AGENT
2Shared conventions: AI_AGENT, then AGENT against a known list
3The /opt/.devin path
4The parent process chain, matched against known executables

Every variable, in order: environment variables. A session that is not a terminal and whose GIT_EDITOR cannot prompt turns masking on without naming an agent. --agent declares an agent session by hand.

Detection reads variables any process can set or unset, so it only changes defaults.

Sealed runs

.env.schema
# @type=string(startsWith=sk_live_, minLength=32) @hosts=api.stripe.com
STRIPE_SECRET_KEY=

Under an agent, penv run is always sealed; you opt in with penv run --sealed.

PartBehavior
The commandHolds a placeholder shaped by the key's type, never the value
Requests to a @hosts hostA proxy inside penv run puts the value into the request line and headers. Bodies keep the placeholder
ResponsesA value echoed back, raw or encoded, returns as the placeholder
Computed @hosts@hosts=url($DATABASE_URL, host) seals to the hosts pinned under [hosts]; a differing host refuses the run (@hosts). We receive the expression as hostsExpr and store it; your machine computes and enforces the hosts
postgres://, redis:// URLsGo through local proxies that log in with the real value. Postgres TLS follows the URL's sslmode; verify-ca checks the host name too, as verify-full does

Controls

ControlMechanism
Guardspenv guard writes each harness's own config: deny rules for .env and .env.*, a hook that runs penv hook, or both, as the harness allows
Sealing@hosts keys become placeholders; always on under an agent
MaskingOn by default in every penv run: penv scrubs every value not marked public, and its base64, hex, percent-encoded and JSON-escaped forms, from child output, and a preload masks inside Node, Bun, Deno and Python. --no-mask and --no-preload are ignored under an agent. Variables: environment variables
Reveal approvalYou approve in the console within 10 minutes; the value prints once. Under a harness that runs penv hook, the hook lets penv reveal through to your approval from 1.0.0-beta.4; 1.0.0-beta.3 refuses it there, so no approval is created
AttributionA request from an agent session carries the harness name and, where the harness sets one, its session id; we stamp both on the audit entry

Next: encryption in the cloud.