Penv Cloud
Start free
Docs
Referencesince 1.0.0

Functions

The url, json, hash and random functions a .env.schema value can call, and the argument form they share.

Functions in computed values. The @env-spec functions (concat, if, fallback, forEnv...) are upstream.

Argument form

name($SUBJECT, address, option=value, ...)
RuleDetail
Subject firstrandom, and json when it builds, have no subject and start at options.
One bare addressOnly in url and json. url($X, host) reads, url($X, host=replica.internal.test) writes; a call never does both.
OptionsEvery other argument is name=value, in any order. A repeated or unknown option is a validation failure naming the function and the option.
Filters${KEY | name(args)} is the function with its subject implied; filters chain left to right. A default cannot hold a |.
FailuresName the key and the part, path or option, never the value.
TaintA result is sensitive when any input is: the subject, a written value or a key=. A key read only to decide (eq, startsWith) taints nothing. Only the public lists declassify, and only a stored value.

url

DATABASE_URL=postgres://app:fakepass@db.internal.test:5432/orders?sslmode=require
DB_HOST=url($DATABASE_URL, host)                              # db.internal.test
REPLICA_URL=url($DATABASE_URL, host=replica.internal.test, port=5433)
DB_HOST=${DB_SECRET | json(database_url) | url(host)}
PartResult for the URL aboveDefault
schemepostgrespublic
hostdb.internal.testpublic
port5432public
originpostgres://db.internal.test:5432public
path/orderssensitive
userappsensitive
passwordfakepasssensitive, always
query.NAMEquery.sslmode is requiresensitive
  • A part the URL lacks is empty; fallback(url($X, port), 5432) supplies one.
  • A URL naming several hosts fails host and port. A value that is not a URL fails every part.
  • Writable: scheme, host, port, path, user, password, query.NAME. A password= value is a reference to another key, never a literal. A written URL is sensitive when its source is.
  • path is sensitive because a webhook URL carries its secret there. Opt in with [url] public.
  • A computed @hosts is allowed: @hosts.
# @assert(if(forEnv(production), true, not(eq(url($DATABASE_URL, host), url(penv(production/DATABASE_URL), host)))), "DATABASE_URL points at the production host")

json

CallDoes
json($X)The whole document, compact; invalid JSON fails.
json($X, db.hosts[0])One field.
json($X, project_id=acme-sandbox)Writes a field, creating it when missing.
json(username=$U, password=$P)Builds an object; no subject starts from {}.
json(db.host=h, db.port=5432)A dotted label creates nested objects.
json(hosts[0]=a, hosts[1]=b)An indexed label creates an array.
json(db=raw($INNER))Embeds JSON that already exists.
DB_SECRET='{"username":"app","db":{"hosts":["db1.internal.test","db2.internal.test"],"port":5432},"tls":true}'
DB_HOST=json($DB_SECRET, db.hosts[0])          # db1.internal.test
DB_HOSTS=json($DB_SECRET, db.hosts)            # ["db1.internal.test","db2.internal.test"]
FEATURES=json(beta=eq($APP_ENV, staging), maxUploadMb=25, region=eu-1)
# {"beta":true,"maxUploadMb":25,"region":"eu-1"}
  • Paths are a.b[0]; ["a.b"] names a key containing a dot. No word is reserved: a field called get is json(get=1).
  • Reading returns a string as is, a number or boolean as text, an array or object as compact JSON. A missing or null field fails; use fallback(). A duplicate key keeps the last value and warns, naming the key.
  • Writing types a bare number, true or false; anything else is a string, and quotes force one (maxUploadMb='25'). Strings are escaped; key order is kept.

hash

API_KEY_FP=hash($API_KEY, algo=sha256, length=8)
TEST_SIG=hash('{"id":1}', algo=hmac-sha256, key=$WEBHOOK_SECRET)
SESSION_KEY=hash(session, algo=hmac-sha256, key=$ROOT)
OptionValuesDefault
algosha256, sha512, hmac-sha256, hmac-sha512required
encodinghex, base64, base64urlhex
lengthAt least 1; keeps the first N charactersall
keyThe secret an hmac-* algorithm signs withnone
  • hash is pure: same inputs, same result; it reads and writes nothing.
  • hmac-* needs key=; the other algorithms refuse it. For HMAC the subject is the data.
  • md5 and sha1 are not offered. Reversible encodings stay filters: base64, b64d, urlencode.
  • A result is sensitive when the subject or the key is. Show it with [hash] public.

random

SESSION_SECRET=random(length=32)               # letters and digits
COOKIE_KEY=random(kind=hex, length=32)
INSTANCE_UUID=random(kind=uuid)                # a version-4 UUID
OptionValuesDefault
kindalnum, hex, base64url, uuidalnum
length8 to 512; required, except uuid takes nonenone
  • Characters come from the operating system's generator.
  • penv run generates the value once and keeps it in .env.local (.env.test.local for test). check, ls and scan never write it; check notes it as pending. penv push never sends it. Each machine has its own.
  • random(32) is a validation failure that says to write length=32.