Reference / Env Vault

Env Vault

Inject env into your deploy with a single bootstrap token; later changes don't need a rebuild. Use getEnv() on the server or useEnv() on the React side — public/private split is automatic.

Overview#

Env Vault provides runtime env management for self-hosted applications. Instead of using process.env, your app authenticates to Sentroy core with a single API key and pulls every registered env. When an admin changes a value, your app picks it up after the cache TTL expires — no Docker rebuild required.

Flow

1. Create a project in the Sentroy admin panel (e.g. my-blog).
2. Add environments (e.g. dev, prod) and variables under the project. Each variable carries a public flag.
3. Generate a token for the project + environment pair. The plaintext is shown only once.
4. Set the token as SENTROY_ENV_API_KEY in your deploy environment.
5. Use getEnv("KEY") or useEnv("KEY") in your app.

Bootstrap#

Single env: SENTROY_ENV_API_KEY. Everything else comes from the vault.

# Coolify (or any deploy environment)
SENTROY_ENV_API_KEY=stk_...
# Optional — defaults to https://sentroy.com
SENTROY_ENV_API_URL=https://sentroy.com

That's it. Every other env (DATABASE_URL, BETTER_AUTH_SECRET, NEXT_PUBLIC_TURNSTILE_SITE_KEY, etc.) lives in the Sentroy admin panel.

Install#

The vault lives under the /vault subpath of @sentroy-co/client-sdk — same package as the mail/storage SDK.

npm install @sentroy-co/client-sdk

Server-side: getEnv()#

Async, in-memory cache (TTL 5 min). One HTTP fetch on first call — every subsequent call is in-process.

import { getEnv, getEnvOrThrow, preloadEnv } from "@sentroy-co/client-sdk/vault"

// Load early at process boot — fail-fast on missing envs
await preloadEnv()

// Async — returns undefined if the env is missing
const dbUrl = await getEnv("DATABASE_URL")

// Throws if missing — config-validation pattern
const turnstile = await getEnvOrThrow("BETTER_AUTH_TURNSTILE_SECRET")

Cache

Default TTL is 5 minutes. For webhook- or admin-driven invalidation use refreshEnvCache(). To change the TTL at runtime use setEnvCacheTTL(seconds).

React: useEnv()#

Inject public envs from a server component during SSR; the client useEnv() hook reads them synchronously.

// app/layout.tsx (server component)
import { getPublicEnvs } from "@sentroy-co/client-sdk/vault"
import { EnvProvider } from "@sentroy-co/client-sdk/vault/react"

export default async function RootLayout({ children }) {
  const envs = await getPublicEnvs()
  return (
    <html>
      <body>
        <EnvProvider envs={envs}>{children}</EnvProvider>
      </body>
    </html>
  )
}
// any client component
"use client"
import { useEnv } from "@sentroy-co/client-sdk/vault/react"

export function CaptchaWidget() {
  const siteKey = useEnv("TURNSTILE_SITE_KEY")
  if (!siteKey) return null
  return <Turnstile siteKey={siteKey} />
}

REST API#

Direct access via curl/fetch, without the SDK.

GET /api/env-vault/fetch

Returns every env in the token's scope (public + private). For server-side use.

curl -H "Authorization: Bearer stk_..." \
  https://sentroy.com/api/env-vault/fetch

GET /api/env-vault/public

Only variables flagged public: true. Browser-safe.

curl -H "Authorization: Bearer stk_..." \
  https://sentroy.com/api/env-vault/public

Encryption#

Variable values are encrypted at rest with AES-256-GCM.

The master key lives on Sentroy core in the SENTROY_ENV_MASTER_KEY env. Plaintext is never written to the database — only ciphertext + nonce + auth tag (v1:iv:tag:cipher base64). Decryption happens in the token-auth fetch response.

# Generate a master key (one-time, store in Coolify env)
openssl rand -base64 32
# Add to platform: SENTROY_ENV_MASTER_KEY=<output>

Audit log#

Every change records who/what/when — never the value itself.

The audit log never stores the value itself; it writes a sha256(plaintext)checksum as before/after. That makes "did the value change?" comparable, while a log compromise will not leak any plaintext.