Fix boolean env parsing — PRIME_SYNC_ENABLED=false was reading as true

z.coerce.boolean() calls Boolean(value), so the string "false" is true. So are
"0", "no" and "off". Every feature flag set to false was silently on.

Caught by reading a startup warning that should not have been there: the
deployment logged "PRIME_SYNC_ENABLED is on but PRIME_API_KEY is unset" while
the .env plainly said false. Had the key been present, PIG would have started
polling a third-party API nobody asked it to poll.

Replaced with an explicit parser accepting 1/true/yes/on, treating an empty or
absent value as the default. Demonstrated both behaviours side by side before
committing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 19:46:21 -07:00
parent 0551e8dd6e
commit 46d7d80f1b
+20 -2
View File
@@ -8,6 +8,24 @@
*/
import { z } from 'zod';
/**
* Parse a boolean from an environment variable.
*
* NOT `z.coerce.boolean()`, which calls `Boolean(value)` — so the string
* `"false"` becomes `true`, along with `"0"`, `"no"` and `"off"`. Every
* feature flag set to `false` would silently be on, which is exactly the
* class of bug that gets discovered in production by reading a startup
* warning that should not have appeared.
*/
const envBoolean = (defaultValue: boolean) =>
z
.string()
.optional()
.transform((value) => {
if (value == null || value.trim() === '') return defaultValue;
return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());
});
const schema = z.object({
DATABASE_URL: z.string().min(1, 'DATABASE_URL is required.'),
@@ -24,10 +42,10 @@ const schema = z.object({
PRIME_API_KEY: z.string().optional(),
PRIME_API_BASE: z.string().default('https://api.primeintellect.ai'),
PRIME_SYNC_ENABLED: z.coerce.boolean().default(false),
PRIME_SYNC_ENABLED: envBoolean(false),
PRIME_SYNC_INTERVAL_MINUTES: z.coerce.number().int().positive().default(30),
PIGGY_ENABLED: z.coerce.boolean().default(false),
PIGGY_ENABLED: envBoolean(false),
ANTHROPIC_API_KEY: z.string().optional(),
PIGGY_MODEL: z.string().default('claude-sonnet-5'),
PIGGY_LEASE_SECONDS: z.coerce.number().int().positive().default(300),