betavoidbase is in public beta. It runs, the API is PocketBase's and is not moving, and the version is still 0.x for everything around that.Help us get it to 1.0

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"),
});
JSON
{
  "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.

WrapperWhere 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 secrets
pb_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 token

One 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.

Found something wrong on this page?

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.