pb_secrets
Every piece of configuration a project has, declared in one file, each key saying who is allowed to read it. The values live beside it in a file that never enters version control, so a project needs no .env at all.
Two files
main.ts is the declaration and belongs in the repository. secrets.json holds the values on your machine and is git-ignored, which voidbase init has already arranged.
// pb_secrets/main.ts
import { defineSecrets, secret, server, browser, local, string, number, boolean } from "@voidbase-cloud/voidbase/secrets";
export default defineSecrets({
SMTP_PASSWORD: secret(string(), "the mail provider's password"),
STRIPE_KEY: secret(string()),
MAX_UPLOAD_MB: server(number().default(10)),
FEATURE_DIGEST: server(boolean().default(false)),
PUBLIC_SITE_URL: browser(string().default("https://example.com")),
VOIDBASE_DEPLOY_CF_API_KEY: local(string(), "the deploy token"),
VOIDBASE_DEPLOY_NAME: local(string().default("blog-api"), "the Worker this project deploys to"),
});{
"SMTP_PASSWORD": "...",
"STRIPE_KEY": "...",
"VOIDBASE_DEPLOY_CF_API_KEY": "..."
}Who may read it
Every key is wrapped in one of four words, and a bare validator is refused. That is deliberate: the person maintaining this file is the one answering for who can see what, so the file makes them say it rather than letting it be inferred.
| Wrapper | Where the value ends up |
|---|---|
secret() | The deployed instance only, as an encrypted secret. Never listed back, never in a build. Stored on the first deploy and never silently replaced afterwards. |
server() | A plain variable on the instance, readable by hooks and routes. Set by every deploy. |
browser() | The same, plus inlined into any client build. Treat it as public, because it is. |
local() | Your own tooling: the deploy token, the Worker's name. Read on your machine and in CI, deployed nowhere. |
Validation, defaults and types
The validators are string(), number(), boolean(), url(), email(), oneOf() and json(), each with .optional() and .default(value). A default is a real value, so a key with one needs no entry in secrets.json and no entry in production either. A deploy stops on a missing or invalid value and names the key, never the value, so a typo is caught before it ships rather than after.
Reading them
// in a hook, the way PocketBase reads configuration
const limit = Number($os.getenv("MAX_UPLOAD_MB"));Locally the declared values, defaults included, are put into the environment when the server starts. On Cloudflare the secrets are the Worker's secrets and the rest are its variables, so the same call works in both places.
Seeing the state of it
voidbase secretspb_secrets: 7 declared (2 secret, 2 server, 1 public, 2 local, never deployed), 3 valued in secrets.json,
worker "blog-api" has 2 of the secrets
SMTP_PASSWORD secret local value on the worker the mail provider's password
STRIPE_KEY secret local value on the worker
MAX_UPLOAD_MB server default 10
FEATURE_DIGEST server default false
PUBLIC_SITE_URL public default "https://example.com"
VOIDBASE_DEPLOY_... local local value the deploy tokenOne row per declared key: its tier, whether it has a value here or a default, and, when the deploy token is available, whether the instance already has it. voidbase secrets push stores local secret values on the Worker, including replacing ones already there, which a deploy deliberately will not do.
secrets.json is the one file in a project that must never be committed. Check your .gitignore has it before the first commit, especially in a repository that did not come from voidbase init.
Fix it yourself. The link below opens this file in GitHub's editor and forks the repository for you if you need one, and your change becomes a pull request without leaving the browser.