@werk/tresor (0.1.1)
Installation
@werk:registry=https://gitlab.amandy.love/api/packages/werk/npm/npm install @werk/tresor@0.1.1"@werk/tresor": "0.1.1"About this package
Tresor
Tresor is a standalone Rust secret service and CLI. tresord runs the HTTP
service; tresor is the operator CLI for storing and using secret refs.
Architecture
tresor/
crates/
tresor-core/ — domain types and CredentialStore trait
tresor-store/ — SQLite backend
tresor-api/ — Axum HTTP server
tresor-cli/ — operator CLI
migrations/sqlite/
Docs
Current security status
New secret writes use owner-bound refs, version rows, audit events, and encrypted
payload storage. The existing /v1/credentials/* routes remain as the Aurora
Relay compatibility surface and are mapped onto the new secret storage model.
The current v1 implementation defaults to OIDC bearer-token validation and
Regelwerk authorization. Legacy service tokens and allow-open modes are explicit
development or compatibility modes. Production payload writes use age-v1
envelope encryption.
API (Aurora Relay contract)
All credential routes require:
Authorization: Bearer <OIDC access token>in production, or a legacy token only whenTRESOR_AUTH_MODE=legacy-token"relayId": "..."in the request body (optional, logged for audit)
| Method | Path | Description |
|---|---|---|
GET |
/health |
Liveness probe — { "ok": true } |
POST |
/v1/credentials/check |
Check whether a credential ref exists |
POST |
/v1/credentials/resolve |
Resolve and return a credential bundle |
PUT |
/v1/credentials |
Store a legacy credential bundle |
The native secret API is available under /v1/secrets:
| Method | Path | Description |
|---|---|---|
GET |
/v1/secrets |
List safe secret metadata |
PUT |
/v1/secrets |
Store a new encrypted secret version |
POST |
/v1/secrets/metadata |
Read safe metadata for one ref |
POST |
/v1/secrets/use |
Resolve a secret for runtime use |
POST |
/v1/secrets/read |
Read raw payload with secret.read |
POST |
/v1/secrets/rotate |
Rotate the active secret payload |
POST |
/v1/secrets/versions |
List version metadata |
POST |
/v1/secrets/audit |
List audit events |
DELETE |
/v1/secrets/delete |
Delete a secret |
Check
POST /v1/credentials/check
{
"credentialRef": "OCI_LOGICALIS",
"expectedType": "oci",
"relayId": "aurora-relay"
}
Response 200:
{ "configured": true, "kind": "oci" }
Response 404 when the ref is not stored.
Resolve
POST /v1/credentials/resolve
{
"credentialRef": "OCI_LOGICALIS",
"expectedType": "oci",
"relayId": "aurora-relay",
"actionContext": {
"subjectId": "user-1",
"action": "oci.execute",
"resourceId": "ocid1.compartment.oc1..abc"
}
}
Response 200:
{
"leaseId": "lease-…",
"expiresAt": "2026-07-07T18:00:00Z",
"bundle": {
"type": "oci",
"data": {
"userOcid": "…",
"tenancyOcid": "…",
"fingerprint": "…",
"region": "eu-frankfurt-1",
"privateKeyPem": "-----BEGIN PRIVATE KEY-----\n…"
}
}
}
Error responses
All errors return a JSON body:
{ "error": "description" }
| Status | When |
|---|---|
401 Unauthorized |
Missing or invalid bearer token |
404 Not Found |
Credential ref not stored |
422 Unprocessable Entity |
Invalid credentialRef or expectedType |
500 Internal Server Error |
Storage error |
Configuration
| Variable | Default | Description |
|---|---|---|
TRESOR_BIND_ADDR |
0.0.0.0:9471 |
HTTP listen address |
TRESOR_DB_PATH |
.tresor/tresor.db |
SQLite database path |
TRESOR_AUTH_MODE |
oidc |
Authentication mode: oidc, dev-open, or explicit legacy-token |
TRESOR_OIDC_ISSUER |
— | OIDC issuer when TRESOR_AUTH_MODE=oidc |
TRESOR_OIDC_AUDIENCE |
— | Expected JWT audience |
TRESOR_OIDC_JWKS_URL |
<issuer>/protocol/openid-connect/certs |
Optional JWKS override |
TRESOR_OIDC_USERNAME_CLAIM |
preferred_username |
User actor claim |
TRESOR_OIDC_SERVICE_CLAIM |
azp |
Service actor claim |
TRESOR_OIDC_ACTOR_REALM |
inferred from issuer | Actor realm segment |
TRESOR_SERVICE_TOKEN |
— | Legacy bearer token, only with TRESOR_AUTH_MODE=legacy-token |
TRESOR_SERVICE_TOKEN_FILE |
— | File containing legacy bearer token |
TRESOR_AUTHZ_MODE |
regelwerk |
Authorization mode: regelwerk or explicit dev-open |
TRESOR_REGELWERK_URL |
— | Regelwerk base URL for /v1/check |
TRESOR_REGELWERK_TIMEOUT_MS |
1500 |
Regelwerk HTTP timeout |
TRESOR_MIGRATIONS_DIR |
auto (dev: workspace-relative, Docker: /app/migrations/sqlite) |
SQL migrations directory |
TRESOR_KEY_PROVIDER |
— | Required key provider mode: age-file or dev-file |
TRESOR_AGE_RECIPIENTS_FILE |
— | age recipients used for new payload encryption |
TRESOR_AGE_IDENTITY_FILE |
— | age identity used for active version decryption |
TRESOR_KEY_FILE |
— | Dev-only key material file for dev-file |
TRESOR_DEFAULT_REALM |
— | Realm used by native and legacy secret routes |
TRESOR_LEGACY_OWNER |
service:tresor:legacy |
Owner assigned to legacy credential writes |
Production should use TRESOR_KEY_PROVIDER=age-file. dev-file is only for
local development and must be selected explicitly.
Build and run
cargo build --release -p tresor-api -p tresor-cli
export TRESOR_AUTH_MODE=dev-open
export TRESOR_AUTHZ_MODE=dev-open
export TRESOR_KEY_PROVIDER=dev-file
printf '0123456789abcdef0123456789abcdef\n' > /tmp/tresor-dev-key
export TRESOR_KEY_FILE=/tmp/tresor-dev-key
export TRESOR_DEFAULT_REALM=default
export TRESOR_DB_PATH=.tresor/tresor.db
cargo run -p tresor-api --bin tresord
The server shuts down gracefully on SIGTERM or Ctrl-C, draining in-flight requests before exit.
Development
Testing
# Run all workspace tests
cargo test --workspace
# Run only API integration tests
cargo test -p tresor-api
# Run with log output
RUST_LOG=debug cargo test --workspace -- --nocapture
Linting and formatting
# Check formatting
cargo fmt --all --check
# Run clippy
cargo clippy --workspace --all-targets -- -D warnings
CLI
export TRESOR_URL=http://127.0.0.1:9471
export TRESOR_OIDC_ISSUER=https://keycloak.example/realms/example
export TRESOR_OIDC_CLIENT_ID=tresor-cli
tresor login
printf '{"password":"app-password"}' | tresor secret put app/postwerk/yahoo-main --type password --owner service:postwerk --from-stdin
tresor secret list
tresor secret metadata app/postwerk/yahoo-main
tresor secret use app/postwerk/yahoo-main
tresor secret versions app/postwerk/yahoo-main
For local compatibility mode, run tresor login --legacy-token <token> against a
daemon started with TRESOR_AUTH_MODE=legacy-token.
Do not pass secret values as command arguments. Use --from-stdin or
--from-file.
Migration
Legacy rows from the deprecated credentials table can be imported once into
owner-bound encrypted secrets:
export TRESOR_KEY_PROVIDER=age-file
export TRESOR_AGE_RECIPIENTS_FILE=/run/secrets/tresor-age-recipients
export TRESOR_AGE_IDENTITY_FILE=/run/secrets/tresor-age-identity
tresor migrate legacy-credentials --owner service:tresor:migration --realm production
The migration is idempotent: refs already present in secrets are skipped. It
refuses non-age providers unless --dev-allow-insecure-key-provider is passed.
Aurora Relay integration
AURORA_CREDENTIAL_BACKEND=tresor
AURORA_TRESOR_URL=http://127.0.0.1:9471
AURORA_TRESOR_ACCESS_TOKEN_FILE=/run/secrets/aurora-relay-oidc-access-token
AURORA_RELAY_ID=aurora-relay
Security
private_key_pemis never included in error responses.- New production secret payloads are encrypted as
age-v1. TRESOR_ENV=productionrequires OIDC auth, Regelwerk authz, andage-file.- Regelwerk denial is fail-closed before active version reads or decryption.
- The legacy
credentialstable remains read-compatible; migration imports rows intosecretsandsecret_versions.
Docker
docker build -t tresor:latest .
docker compose -f deploy/docker-compose.tresor.yml up