Secrets are encrypted key-value pairs for storing sensitive configuration data like API keys, database passwords, and authentication tokens.
Background
Secrets provide secure storage that is:
- Encrypted at rest — Stored with AES-256 encryption
- Encrypted in transit — All API calls use HTTPS/TLS
- Injected at runtime — Available as environment variables in your functions
- Organization-scoped — Shared across all functions in your organization
- Never displayed — Secret values are never shown in CLI output or logs
Adding Secrets
Use the CLI to store sensitive data:
# Add a secret
telnyx-edge secrets add DATABASE_PASSWORD "super-secret-password"
# Add multiple secrets
telnyx-edge secrets add PAYMENT_API_KEY "sk_live_abc123"
telnyx-edge secrets add JWT_SECRET "your-jwt-signing-key"
Listing Secrets
$ telnyx-edge secrets list
SECRET ID SECRET NAME CREATED AT
------------ ---------------- -----------------
1f4cafea-21ce-4e2b-9740-17d971c3d892 DATABASE_PASSWORD Nov 13, 2025, 21:17
21ef7449-edbb-4248-b869-3d1352563a64 PAYMENT_API_KEY Nov 13, 2025, 01:02
a209b1b3-062c-46f6-a2f2-3b0061751190 JWT_SECRET Nov 13, 2025, 01:01
Secret values are never displayed for security.
Updating Secrets
Use the same add command to update an existing secret:
# Update with new value
telnyx-edge secrets add DATABASE_PASSWORD "new-password-456"
✅ Secret 'DATABASE_PASSWORD' updated successfully
Deleting Secrets
telnyx-edge secrets delete OLD_API_KEY
✅ Secret 'OLD_API_KEY' deleted successfully
Accessing Secrets in Your Code
Secrets are injected as environment variables when your function runs.
JavaScript
Go
Python
Java
async function handler(request) {
// Secrets are available as environment variables
const dbPassword = process.env.DATABASE_PASSWORD;
const apiKey = process.env.PAYMENT_API_KEY;
if (!dbPassword) {
return { error: "DATABASE_PASSWORD not configured" };
}
// Use secrets to connect to services
const db = await connectDatabase({ password: dbPassword });
return { status: "connected" };
}
package main
import (
"os"
"database/sql"
"fmt"
)
func handler(w http.ResponseWriter, r *http.Request) {
// Secrets are available as environment variables
dbPassword := os.Getenv("DATABASE_PASSWORD")
apiKey := os.Getenv("PAYMENT_API_KEY")
if dbPassword == "" {
http.Error(w, "DATABASE_PASSWORD not configured", 500)
return
}
// Use secrets to connect to services
db, err := sql.Open("postgres", fmt.Sprintf(
"host=db.example.com password=%s", dbPassword,
))
json.NewEncoder(w).Encode(map[string]string{"status": "connected"})
}
import os
def handler(request):
# Secrets are available as environment variables
db_password = os.environ.get("DATABASE_PASSWORD")
api_key = os.environ.get("PAYMENT_API_KEY")
if not db_password:
return {"error": "DATABASE_PASSWORD not configured"}
# Use secrets to connect to services
db = connect_database(password=db_password)
return {"status": "connected"}
import javax.ws.rs.*;
import javax.ws.rs.core.*;
@Path("/")
public class SecureResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response connect() {
// Secrets are available as environment variables
String dbPassword = System.getenv("DATABASE_PASSWORD");
String apiKey = System.getenv("PAYMENT_API_KEY");
if (dbPassword == null) {
return Response.status(500)
.entity("{\"error\": \"DATABASE_PASSWORD not configured\"}")
.build();
}
// Use secrets to connect to services
Database db = connectDatabase(dbPassword);
return Response.ok("{\"status\": \"connected\"}").build();
}
}
Secrets vs Environment Variables
Use Secrets 🔒 (sensitive data)
- Database credentials —
DATABASE_URL, DATABASE_PASSWORD
- External API keys —
PAYMENT_API_KEY, EMAIL_SERVICE_KEY
- Authentication tokens —
JWT_SECRET, OAUTH_SECRET, WEBHOOK_SECRET
- Encryption keys —
ENCRYPTION_KEY, COOKIE_SECRET
Use Environment Variables 🌍 (non-sensitive config)
- Application settings —
LOG_LEVEL, MAX_CONNECTIONS, TIMEOUT_MS
- Feature flags —
ENABLE_CACHING, DEBUG_MODE
- Public endpoints —
API_BASE_URL, CDN_URL
See Environment Variables for non-sensitive configuration.
Secret Rotation
Rotate secrets regularly for security compliance:
# 1. Update the secret with new value
telnyx-edge secrets add DATABASE_PASSWORD "new-secure-password"
# 2. Redeploy functions to pick up the new value
telnyx-edge ship
# 3. Verify the function works with new credentials
curl https://your-function.telnyxcompute.com/health
When to rotate:
- Monthly for security compliance
- After suspected credential compromise
- After team member offboarding
- When third-party services rotate their keys
Naming Conventions
Use clear, descriptive names:
| ✅ Good | ❌ Avoid |
|---|
DATABASE_PASSWORD | PASSWORD |
STRIPE_API_KEY | API_KEY |
JWT_SIGNING_SECRET | SECRET |
SENDGRID_API_KEY | EMAIL_KEY |
Best practices:
- Use
UPPER_SNAKE_CASE
- Include service name:
STRIPE_API_KEY, TWILIO_AUTH_TOKEN
- Use standard suffixes:
_KEY, _SECRET, _TOKEN, _PASSWORD
Local Development
🔜 Coming soon — Local development with secrets (.env file support) is planned for a future release.
For now, set environment variables manually when testing locally:
# Linux/macOS
export DATABASE_PASSWORD="local-test-password"
python your_function.py
# Or inline
DATABASE_PASSWORD="test" python your_function.py
Per-Environment Secrets
🔜 Coming soon — Per-environment secrets (dev/staging/prod) are planned for a future release.
For now, use naming conventions to separate environments:
# Development
telnyx-edge secrets add DEV_DATABASE_PASSWORD "dev-password"
# Production
telnyx-edge secrets add PROD_DATABASE_PASSWORD "prod-password"
Then reference the appropriate variable in your code based on an environment flag.
Troubleshooting
Secret not found in function
# Check that the secret exists
telnyx-edge secrets list
# If missing, add it
telnyx-edge secrets add MY_SECRET "value"
# Redeploy the function
telnyx-edge ship
Secret value seems wrong
# Update the secret
telnyx-edge secrets add MY_SECRET "correct-value"
# Redeploy to pick up new value
telnyx-edge ship
Permission denied
# Check authentication
telnyx-edge auth status
# Re-authenticate if needed
telnyx-edge auth login