Penv Cloud
Start free
Docs
Guidesince 1.0.0-alpha.3

Adopt an existing app

Turn your .env into a committed .env.schema and .penv/config.toml, generate typed access, write agent guards, then push the values to us.

You finish with .env gone from your working tree and .env.schema plus .penv/config.toml committed. The keys in the examples are from our own repository.

Write the schema

penv init

penv init reads .env, writes .env.schema, and adds .env to .gitignore. With no .env present it writes an empty one; add your keys, then run penv init --force.

At a terminal it also offers a picker over every known agent harness (installed ones preselected) and generates the typed file for each language in the repository.

KEY                TYPE     REQUIRED  SENSITIVE
DATABASE_URL       url      yes       yes
STRIPE_SECRET_KEY  string   yes       yes
PORT               port     no        no

Every key defaults to sensitive and required unless a bundler prefix or a non-secret-looking value says otherwise. A key named like a credential never has its value copied. From a dotenv file lists the inference rules.

Correct the guesses

.env.schema
# @penv=acme/api-gateway

# @type=url
DATABASE_URL=

# @type=string(startsWith=sk_) @rotate=90d
STRIPE_SECRET_KEY=

# @type=url @sensitive=false
NEXT_PUBLIC_APP_URL=http://localhost:3000

# @type=port
PORT=3000

Decorators follow @env-spec (see item decorators and data types). penv deviation: an unknown decorator is an error. @penv=, @rotate=, @hosts, @assert and @currentEnv are penv additions; see schema format.

A secret key keeps its decorators and an empty value, as DATABASE_URL and STRIPE_SECRET_KEY do above; the value lives in a value file or in Penv Cloud, never in the schema.

Check

penv check
penv check --strict

penv check validates the schema and values, reads your code, and reports guard coverage. --strict fails on variables your code reads that the schema does not declare.

FindingResult
Schema violation or failed @assertExit 3, naming the key
Key both bundler-prefixed and @sensitive, or a public key built from a secretExit 3
Value file holding a secret that git tracks or does not ignoreExit 3
Variable the code reads and the schema does not declareReported; exit 3 with --strict
Declared key no source file mentionsReported, not failed
Key in .env with no schema block (drift)Reported, not failed
Stale or missing guard fileReported, not failed

A bundler-prefixed value reaches the browser whatever the schema says. Choose the prefix or @sensitive, not both.

Generate typed access

penv gen ts

penv gen asks once for the output path and records it, with the target's options, in .penv/config.toml:

.penv/config.toml
[targets.ts]
output = "apps/web/src/env.ts"

[targets.ts.options]
key_case = "camel"
mask = true
runtime = "node"

A .penv/targets/<name>/target.toml folder from before beta.2 still reads; the next penv gen of that target moves it into [targets.<name>] and deletes the folder.

The file exports one env. Secrets are read by computed name, so no bundler inlines them, and with mask = true the file masks sensitive values in console and Response bodies. penv never edits tsconfig.json or package.json; it prints the import line to paste. See typed access for your language.

Write the harness rules

penv guard

penv guard writes each installed harness's rules into its own config, merging without weakening existing rules. See keep agents out of .env.

Verify locally

penv run -- <dev command>

penv run starts your app with the resolved values, masking them in its output.

Push the values

penv login
penv push

penv login prints an eight-character code and a page to open, then stores your credential in the OS keychain. penv push sends the shared value files for the environment (.env, .env.<env>) with their decorators, then deletes them. *.local files stay on your machine.

Casepush behavior
No @penv= headerpenv names the project; we create it with development plus the pushed environment; penv writes the slug into the header
You belong to several workspacesRefuses and lists them; pass penv push --org <slug>
--org differs from the headerorg_mismatch, exit 3
No value in any filenothing_to_push, exit 3
Key with no valueUpdates the stored schema, writes no version
Value with no schema blockSent with no schema invented

penv pull writes .env again from your workspace.

Commit .env.schema and .penv/config.toml.

Staging and production