Repository navigation
prisma dev reads secrets from the environment and never invents them - #382
wmadden-electric wants to merge 13 commits into
Conversation
…vents them The dev preflight no longer mints placeholder secrets or stores values in .prisma-composer/dev/secrets.json. For each service it validates the input with every unset or empty secret left out. If the schema rejects that, dev stops with an error naming each missing variable, the service, the schema message, and how to set it (shell export or a .env loaded from prisma.config.ts). Optional unset secrets are left out of the input document. The preflight returns a payload that the CLI carries to the alchemy process on the preflight transport: the absent secret names, and a SHA-256 hash of each pointed variable value. The serializer leaves a secret out only when that payload names it; deploys send none, so their documents are unchanged. The local Deployment provider reads current values from process.env and gives each service only the variables its own rows point at. A leftover secrets.json is deleted when prisma dev starts. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A validator that throws (for example an async schema) again fails deploy with an error that names the service. The dev preflight reports an input that is invalid for reasons other than missing secrets next to the other problems, in dev wording, instead of throwing the deploy error at once. The env-param error now gives both ways to set a value, and the ways to set a value are printed once. Adds a test that the local Deployment provider reads the same pointer formats the serializer writes, and a test that the compute descriptor leaves out a secret the dev payload names absent. The compute descriptor passes process.env to the serializer explicitly. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under prisma dev, the <name>:updatedAt trigger member of each Deployment now holds the SHA-256 fingerprint the dev preflight carries for that variable, so it changes exactly when the value changes. The local Deployment provider diff returns nothing when the artifact is unchanged, so the engine compares the props, sees the changed trigger, and reconciles; the provider then gives the emulator the new environment. Deploy still reads platform timestamps. The local-dev fixture gains an optional secret and loads a .env from its own prisma.config.ts. The criteria 4 and 5 integration test is rewritten: a required secret that is unset or empty stops dev, an unset env-sourced param stops dev, a value loaded by prisma.config.ts reaches the service, an unset or empty optional secret arrives absent, and a changed value reaches the service on a warm start. The local-dev integration test now hands the alchemy process the preflight payload, as the CLI does. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Alchemy writes trigger members to its state as given, so a bare SHA-256 of a secret value ended up in the project, where a guessable value could be recovered. The dev preflight now uses HMAC-SHA256 with a random 32-byte key that it creates once in .prisma-composer/dev/fingerprint.key (mode 0600) and reuses. Losing the key causes one redeploy; --fresh deletes it. The criteria 4 and 5 integration test now writes a leftover secrets.json before a dev run and checks that the run deletes it. Comments in the compute descriptor no longer claim that Alchemy stores only a salted hash of the triggers. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…0056) ADR-0056 amends ADR-0041: prisma dev takes secrets only from the shell or a .env loaded by prisma.config.ts, a required unset secret is an error, an optional one is absent, nothing is stored, and a changed value redeploys through an HMAC fingerprint in the trigger members. Deploy is unchanged. The ADR index, ADR-0041, local-dev.md, running-locally.md and the core-concepts skill no longer describe placeholder secrets. The preflight error now leads with import dotenv/config and says process.loadEnvFile works only under Node, because Bun does not implement it. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he docs examples/auth, examples/email and examples/storefront-auth each stop under prisma dev now that a required secret is no longer made up: auth and email need EMAIL_DELIVERY_CREDENTIAL (plus the EMAIL_DELIVERY_MODE and EMAIL_FROM params they always needed), storefront-auth needs AUTH_SIGNING_SECRET. Each gets a committed .env.example with harmless local values, dotenv as a dev dependency, and import dotenv/config at the top of its prisma.config.ts. .env stays ignored by the root .gitignore. The guide, the skill and the preflight error now say to install dotenv before importing it. ADR-0056, ADR-0041, the ADR index and the guide no longer claim that no value is stored: Composer never reads a value back from an earlier run, and ADR-0056 records that the compute emulator keeps a copy of each running service environment. The value-sourcing paragraph in local-dev.md is split and drops project shorthand. .drive/deferred.md no longer describes secrets.json as current and is committed with the entries for this slice. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l code review The deploy and dev payloads are decoded in one place, control/preflight-payload.ts, which returns a tagged result. The lowering reads one preflight field on its options: pointerVersion (the platform updatedAt on deploy, an HMAC of the value under prisma dev) and absentSecrets as a plain set. The persisted <name>:updatedAt trigger key is unchanged, so hosted deployments do not redeploy. serializeInput takes an options object, so the compute descriptor no longer reads the environment. The fingerprint key moves to dev-secrets.ts as readOrCreateFingerprintKey. It is linked into place, so two first runs agree on one key, and a damaged key stops dev with the file path and the fix. The dev error starts with "prisma dev cannot start". execute-dev blanks the transport variable of every configured extension, as deploy does. Comments no longer say SHA-256, and the core transport header says it carries either preflight. Tests: the CLI carries the dev payload and blanks stale variables; a secret shared by two services; a damaged key; the deploy cases moved next to the serializer and the payload decoder; the criteria 4 and 5 integration test checks that a changed secret redeploys bkg and leaves web alone. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ADR-0056 A .env loaded by prisma.config.ts is read by every prisma command. prisma deploy uploads any secret the target stage does not have yet from it, so running-locally.md, local-dev.md and ADR-0056 now say to keep local-only values out of it before deploying, or to deploy from a shell where it is not loaded. ADR-0056 now states that env-sourced params get the same sourcing, that dev and deploy write different input documents for an unset optional secret until deploy gets the same fix, that the lowering takes two dev inputs (absent names and fingerprints), that the <name>:updatedAt key name is kept on purpose, and that a service now receives only the variables it points at. The guide says the same about the narrower environment. The ADR-0041 header line says which of its passages were updated, and the ADR index line matches. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ockfile Each .env.example now says that prisma deploy from the same directory reads it and uploads any secret the target stage does not have yet. storefront-auth leaves AUTH_SIGNING_SECRET empty, so a developer must choose one and the published value can never become a hosted signing key; prisma dev stops with an error naming it until then. The email examples keep EMAIL_DELIVERY_MODE=none and say that deploying with this .env loaded gives the stage a mode that sends no email. The lockfile diff against main is now only the three dotenv entries; the unrelated alchemy peer reshuffles for examples/bucket and examples/cron are reverted. pnpm install --frozen-lockfile accepts it. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
serializeInput has its doc comment back. resolveInputBinding and inputValidationFailure take the same options object as serializeInput ({ env, absentSecrets }), with one place supplying the defaults.
Creating the fingerprint key falls back to writeFile with flag wx when the file system refuses hard links, and treats an existing file the same way. A new test races eight first runs and checks they all get one key.
ADR-0056 says how loading .env in Composer would compare on the deploy upload risk, and why it was still rejected. The deferred entry for the copied wire format names s3-protocol as the model.
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
✅ Gizmo reviewed b9efe0a — posted 0 inline comment(s) this pass. Open findings: none Change walkthrough
Dev preflight and serializer. Required-vs-optional is decided by validating each service's input with unset secrets left out (ADR-0042 compliant — the schema is never read). Absent names travel to the deploy-engine process on the existing Examples and docs. This pass's delta. The incremental diff is dominated by the rebase onto main's ADR-0056 dev-server work and ADR-0057 addressing work, and it all coheres with the secrets change: |
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
Main took ADR-0056 for the dev-server decision. The secrets decision becomes ADR-0057, and both amendments are kept on ADR-0041 and its index line. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
commit: |
Brings in dev servers (#381), full node addresses (#384, ADR-0057), the deploy entry in prisma.config.ts (#387) and prisma deploy list (#388). The secrets decision becomes ADR-0058, and every reference to it moves with it. A dev server gets its environment from materializeEnv, so it receives the same per-service secret values from process.env, an unset optional secret is absent, and a changed value restarts it; the serve test now covers both. The docs that described placeholders for dev servers now describe reading secrets before the dev server starts. Tests and expected error text use full addresses. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
prisma devno longer invents secrets. A secret now comes only from the environment of the current run: the shell, or a.envthatprisma.config.tsloads. A required secret that is unset stops the run:An optional secret that is unset now reaches the service as absent, so a feature that depends on it stays off. A secret whose value changed since the last run restarts the services that read it.
Closes #379. Closes #380.
The problem
Until now,
prisma devgave any secret the shell left unset or empty a made-up value,local-placeholder-<16 hex>, and saved it in.prisma-composer/dev/secrets.json. A service could not tell the made-up value from a real key. prisma/asks declares its Fireworks key optional ('fireworksApiKey?': secretString()) and turns embeddings off without it. Locally it received the made-up key, turned embeddings on, and every search failed with a 401 from Fireworks. prisma/asks had to recognise the placeholder by its text (prisma/asks#233). Because the value was saved and a warm start does not redeploy, a real key set later never reached a running service (#380).What changes
target/src/local-target/preflight.ts). The placeholder and its warning are gone.prisma devstops and names each secret whose absence alone fails validation, with the service and the schema's message.PRISMA_COMPOSER_PREFLIGHT_<extension>), asdev:followed by JSON. The serializer drops anenvSecretfrom the input document only when its name is on that list. Deploy never sends the list, so dev and deploy still share one lowering (ADR-0041). One decoder reads both payload kinds:target/src/control/preflight-payload.ts.secretsStoreis removed. The localDeploymentprovider reads current values fromprocess.envand gives each service only the secrets and env-sourced params its own rows point at (local-target/src/compute.ts). A leftoversecrets.jsonis deleted on start.<name>:updatedAtmember to eachDeployment's triggers for every variable it points at. Underprisma devthat member is now an HMAC-SHA256 of the current value, under a random key in.prisma-composer/dev/fingerprint.key(mode 0600). When the value changes, Alchemy sees the trigger change and updates that deployment; other services are left alone. The key exists because Alchemy writes trigger members to its state file as given, and a bare hash would let someone check guesses against it.local-dev.md,running-locally.mdand the core-concepts skill describe the new behaviour.examples/auth,examples/emailandexamples/storefront-authstopped underprisma devwithout their secrets. Each now has a committed.env.example,dotenvas a dev dependency, andimport 'dotenv/config'in itsprisma.config.ts.Breaking for local dev
.envfromprisma.config.ts.process.envnow gets nothing.prisma deployalso evaluatesprisma.config.ts. A.envloaded there is in the deploy's environment, and deploy uploads any secret the target stage does not have yet. Keep local-only values out of it before deploying, or deploy from a shell where it is not loaded. The guide, ADR-0058 and each example's.env.examplesay so.Deploy behaviour does not change.
Tests
local-target-preflight.test.tsis rewritten: required unset or empty stops dev with the variable and the service; optional unset or empty is absent from the document; a secret shared by two services; the payload never carries a value; a damaged key file gives the path and the fix; two runs creating the key at once agree.input-serializer.test.tsandpreflight-payload.test.ts: deploy documents and deploy payload decoding are unchanged.compute-materialize-env.test.tsanddev-pointed-variables.test.ts: services get current values, only their own, and the copied pointer formats match the serializer's.operations.test.ts:prisma devcarries the payload to the engine and blanks a stale one.test/integration/test/local-dev-criteria-4-5.integration.ts, criterion 5 rewritten end to end: required unset and empty stop dev; a value from the fixture's.envreaches the service with nothing in the shell; an optional secret arrives absent; after a warm start with a changed value only the service that reads it is redeployed and sees the new value; a leftoversecrets.jsonis deleted.examples/storefront-auth, reading each service process's environment: unset stops with the error above; value A reachesauth.serviceand not the storefront; a warm start with value B givesauth.servicevalue B; a value only in.envreaches the service.Follow-up work
Recorded in
.drive/deferred.md. The main item: a deploy still fails when an optional secret is missing on the platform. ADR-0042 says that case should deploy with the field left out.Alternatives considered
validate.dev:prefix keeps deploy's payload shape unchanged..envitself underprisma devonly. This would keep local values out ofprisma deploy. Rejected because Will chose the ORM's model, where the user owns their environment andprisma.config.tsloads it.Agent: schubert-65
🤖 Generated with Claude Code