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 initpenv 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 noEvery 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
# @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=3000Decorators 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 --strictpenv 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.
| Finding | Result |
|---|---|
Schema violation or failed @assert | Exit 3, naming the key |
Key both bundler-prefixed and @sensitive, or a public key built from a secret | Exit 3 |
| Value file holding a secret that git tracks or does not ignore | Exit 3 |
| Variable the code reads and the schema does not declare | Reported; exit 3 with --strict |
| Declared key no source file mentions | Reported, not failed |
Key in .env with no schema block (drift) | Reported, not failed |
| Stale or missing guard file | Reported, 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 tspenv gen asks once for the output path and records it, with the target's
options, in .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 guardpenv 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 pushpenv 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.
| Case | push behavior |
|---|---|
No @penv= header | penv names the project; we create it with development plus the pushed environment; penv writes the slug into the header |
| You belong to several workspaces | Refuses and lists them; pass penv push --org <slug> |
--org differs from the header | org_mismatch, exit 3 |
| No value in any file | nothing_to_push, exit 3 |
| Key with no value | Updates the stored schema, writes no version |
| Value with no schema block | Sent with no schema invented |
penv pull writes .env again from your workspace.
Commit .env.schema and .penv/config.toml.