TILLSECRETS · OVERVIEW

Encrypted config. Credentials that expire.

GA

TillSecrets is where your configuration and secrets live — encrypted at rest under a key only your org holds, versioned so every change is reversible, and pulled at boot over one authenticated call. It carries two kinds of secret under one model: static config (a value you set and version) and dynamic secrets (a short-lived credential leased on demand and auto-revoked on expiry). Same workspace, same login, same audit log as the rest of TillDev.

01Mental model

Two kinds of secret, one model

Every secret is a KEY — env-var-style, like STRIPE_KEY or DATABASE_URL — that lives in one environment. What differs is its kind:

KindWhat it holdsHow you read it
Static configA value you set. Stored as an immutable, envelope-encrypted version; changing it allocates a new version and rollback re-points to an older one.Returned in a pull, decrypted.
Dynamic secretA reference to a backend (Postgres today). No value is stored — a fresh, short-lived credential is minted on demand and auto-revoked on expiry.Leased from the backend service, once.

Both sit in the same hierarchy: your org owns projects, a project has environments (dev / staging / prod), and an environment holds the keys. A service token is scoped to a project — and optionally pinned to one environment — so a prod token can never read dev.

text
org
└─ project           "acme-api"
   └─ environment     dev · staging · prod
      └─ KEY          STRIPE_KEY, DATABASE_URL, …
         ├─ static  → immutable versions (v1, v2, v3 …) + a "current" pointer
         └─ dynamic → a backend reference; credentials are leased, never stored
02Pulling

Pull the environment at boot

Apps don’t ship secrets in their bundle or a .env file — they pull the environment at startup with a ts_… service token. On Node the one-liner injects everything into process.env:

server.ts
// The very top of your server entry, before you read any config.
import { load } from '@tilldev/secrets-node'

// Reads TILLSECRETS_TOKEN (+ optional TILLSECRETS_URL / TILLSECRETS_ENV),
// pulls the environment, and injects each KEY into process.env.
await load()

// process.env.STRIPE_KEY is now populated — nothing was committed to disk.

Under the hood every SDK does the same thing: a single POST /api/secrets/pull that returns the decrypted { secrets, dynamic } map for the token’s environment. The three packages — @tilldev/secrets-node, @tilldev/secrets-edge, and the @tilldev/secrets-core they both wrap — are covered in the config guide. Start from the Quickstart for the full loop.

03Dynamic

Credentials that expire on their own

A dynamic secret never has a stored value. You register a backend (a Postgres database, today) once, then declare a key that references it. When an app needs a credential it leases one: TillSecrets opens a connection to your database, runs CREATE ROLE … VALID UNTIL with your grants, and hands back a username and password that stop working when the lease expires — dropped automatically. We store a reference to the lease, never the credential.

That means a leaked database password has a blast radius measured in minutes, and every app instance gets its own credential. Full walkthrough in Dynamic secrets.

04Sync

Sync to your own platform

If a workload can’t call the pull API — a build step, a platform that only reads its own env vars — TillSecrets can push an environment’s values into your own platform instead. The targets are vendor-agnostic:

Cloudflare · Vercel · Railway · GitHub · AWS SSM · dotenv

Pick a target, and TillSecrets writes the environment’s current values to it — so the source of truth is still TillSecrets, but your platform sees plain env vars. No lock-in: the same environment can fan out to more than one target. See Sync targets.

05Security

Encrypted end to end

This is the whole pitch, so it’s worth stating plainly:

  • Envelope encryption. Every value is AES-256-GCM encrypted under a data key (DEK) unique to your org, which is itself wrapped by a master key (KEK). Postgres holds only ciphertext; plaintext is assembled in memory only on an authorized read.
  • Everything is audited. Every read, write, rollback, token mint or revoke, and lease issue or revoke is appended to a tamper-evident log.
  • Tokens are bearer-only. ts_… tokens are stored sha256-hashed, never appear in a URL, and are revocable and optionally expiring.
Server secret
A ts_… token can decrypt an environment. Keep it in a server-side environment variable — never in a browser bundle, an app binary, or a committed file. Prefer a read-scoped token pinned to one environment for anything an app boots with.

The full model — the DEK/KEK split, the audit actions, SSRF guards on dynamic backends — is in Security.


Start with the Quickstart, then read the Config store, Dynamic secrets, and Security. TillSecrets is part of TillDev — one workspace, one login, one audit log. Prefer the pitch? See the product page.