Skip to content

tegata

Secrets lifecycle as code for cloud secret stores.

tegata creates secrets (random keys, API tokens for services such as Cloudflare and Tailscale, database passwords), stores them in a cloud secret store, gives each server or application read access to exactly the secrets you list for it, rotates them, cleans up the old ones, and fills in configuration templates on the servers.

Everything is declared in one YAML file. No server to run, no Kubernetes required.

Cloud secret stores and Vault-style servers are good at storing secrets. Rotating secrets that belong to other services is usually left to you: write a rotation function per service, or do it by hand in each dashboard. tegata puts the whole lifecycle in one place:

Create

A resource names a provider (random, cloudflare, tailscale, …) and its settings. tegata apply creates what does not exist yet.

Store and grant

Values go to Secret Manager. Each secret names its readers (identities: the service accounts of servers, applications, CI workflows), and only they can read it.

Rotate and clean up

tegata rotate makes a new generation; after the servers pick it up, tegata prune deletes what the provider created for older generations (for example, the old API token) and disables the old versions.

Fill in

On a server, tegata render replaces __NAME__ in templates with the stored values.

Deliver

Copies of secrets can also go to GitHub Actions secrets (of a repository or an environment). tegata writes them again whenever the value changes, so CI picks up a rotation on its next run.

Providers are separate executables that speak JSON over stdin and stdout, so you can add one in any language without changing tegata.

To try it on your machine without a cloud account, see examples/minimal.

tegata.yaml
stores:
main:
gcp:
project: my-project
location: us-central1 # where secret replicas live (user-managed replication)
identities: # who reads secrets: names for IAM members
app: [serviceAccount:app-vm@my-project.iam.gserviceaccount.com]
providers: # external providers (random, manual and value are built in)
cloudflare:
source: github.com/k-kawa/tegata # installed by `tegata init`
version: "^0.3"
config: { account_id: 0123456789abcdef }
credentials: # never write secrets in the YAML
api_token: { env: TEGATA_CLOUDFLARE_TOKEN }
resources:
session_key:
provider: random
config: { bytes: 32 }
stores:
main: # where it is stored, under what name, and who reads it
- { name: SESSION_KEY, readers: [app] }
previous: { name: PREVIOUS_SESSION_KEY } # rotate copies the current value here first
releases_read:
provider: cloudflare
type: api_token
config:
name: releases read
policies:
- permissions: [Workers R2 Storage Bucket Item Read]
r2_buckets: [releases]
stores:
main: # one entry per provider output
- { output: id, name: RELEASES_ACCESS_KEY_ID, readers: [app] }
- { output: r2_secret_access_key, name: RELEASES_SECRET_ACCESS_KEY, readers: [app] }
templates/app/app.env
SESSION_KEY=__SESSION_KEY__
PREVIOUS_SESSION_KEY=__PREVIOUS_SESSION_KEY__
RELEASES_ACCESS_KEY_ID=__RELEASES_ACCESS_KEY_ID__
RELEASES_SECRET_ACCESS_KEY=__RELEASES_SECRET_ACCESS_KEY__
Terminal window
tegata init # install the providers, pinned in tegata.lock
tegata check app templates/app # providers, and app can read every name in templates/app
tegata apply # containers, read access, missing values
tegata status # generations, enabled versions, readers
# on the server (no YAML needed; on GCP VMs the project comes from the metadata server)
tegata render templates/app /etc/app

Rotating a key:

Terminal window
tegata rotate session_key # new generation; the old value moves to PREVIOUS_SESSION_KEY
# ... re-run `tegata render` on the servers and restart the services ...
tegata settle session_key # PREVIOUS_SESSION_KEY = SESSION_KEY (the old value is no longer accepted)
tegata prune session_key # disable the old versions
Provider Types What it makes
random (built in) Random bytes as hex (bytes, default 32)
manual (built in) A value you type in (tegata set)
value (built in) data Fixed, non-secret values written in the YAML
tegata-provider-cloudflare api_token, tunnel_token Account API tokens (also usable as R2 keys), Tunnel tokens
tegata-provider-tailscale auth_key Auth keys for joining a tailnet
tegata-provider-aws ses_smtp_credentials Amazon SES SMTP credentials (an access key for an IAM user)
tegata-provider-gcp sql_user Cloud SQL built-in user passwords
tegata-provider-terraform outputs (data) Values read from Terraform outputs

Settings, outputs, and rotation behavior of each: providers. Writing a provider: the provider protocol.

Functional Source License, Version 1.1, ALv2 Future License (FSL-1.1-ALv2).

In short: you can use, modify, and redistribute tegata for any purpose except offering it to others as a commercial product or service that competes with it. Using tegata inside your company, for your own infrastructure, or in professional services for your clients is fine. Each version becomes available under the Apache License 2.0 two years after it is released.

FSL is a Fair Source license, not an OSI-approved open source license. If your organization only allows OSI-approved licenses, check with them before using tegata.