Variables & Secrets
Manage configuration and sensitive data across your organization with a powerful five-level hierarchy and inheritance system.
Hierarchy & Inheritance
SeaGit uses a five-level hierarchy for variables and secrets, allowing you to define configuration once and inherit it across your entire infrastructure.
Level 1: Organization
Global configuration shared across all environments, clusters, applications, and instances.
Example: AWS_REGION, LOG_LEVEL, COMPANY_NAME
Level 2: Environment
Shared across all clusters and applications in this environment (dev, staging, prod).
Example: DATABASE_HOST, API_ENDPOINT, DOMAIN_NAME
Level 3: Cluster
Specific to a Kubernetes cluster within an environment.
Example: CLUSTER_REGION, NODE_TYPE, K8S_VERSION
Level 4: Application
Shared across all instances of an application template.
Example: APP_NAME, DEFAULT_TIMEOUT, CACHE_TTL
Level 5: Instance
Specific to a single application instance. Highest priority - overrides all other levels.
Example: REPLICAS, MEMORY_LIMIT, FEATURE_FLAGS
Inheritance Rules:
- • Variables cascade down from Organization → Environment → Cluster → Application → Instance
- • Child levels inherit parent values automatically
- • Child levels can override parent values by defining same variable name
- • Instance level has highest priority (final override)
- • Secrets follow same inheritance rules as variables
Example Inheritance:
Organization: LOG_LEVEL=info, MAX_CONNECTIONS=100
Environment: LOG_LEVEL=debug (overrides org)
Cluster: DATABASE_HOST=db.cluster1.local
Application: APP_NAME=api-service
Instance: LOG_LEVEL=error (overrides both org and env)
Final values in instance:
LOG_LEVEL=error # From instance (highest priority)
MAX_CONNECTIONS=100 # From organization (inherited)
DATABASE_HOST=db.cluster1 # From cluster (inherited)
APP_NAME=api-service # From application (inherited)Variables vs Secrets
Variables
- Visibility: Visible in UI and API responses
- Use Cases: Non-sensitive configuration
- - Feature flags
- - API endpoints
- - Port numbers
- - Environment names
- - Log levels
- Storage: Plain text in database
- Access: Viewable by all users with access
Secrets
- Visibility: Hidden (shown as ****)
- Use Cases: Sensitive data
- - Database passwords
- - API keys
- - TLS certificates
- - Encryption keys
- - OAuth tokens
- Storage: Encrypted at rest
- Access: Write-only via UI (cannot view after creation)
Important:
Both variables and secrets are injected as environment variables into your containers. The distinction is only in how they're stored and displayed in SeaGit. Applications access them identically using process.env.VARIABLE_NAME.
Managing Variables
Adding Variables
- 1. Navigate to the desired level (Organization, Environment, Application, or Instance)
- 2. Go to the Variables tab
- 3. Click Add Variable
- 4. Enter key and value
- 5. Click Save
Variable Naming Conventions
✓ GOOD:
DATABASE_URL # Clear, descriptive
MAX_RETRIES # SCREAMING_SNAKE_CASE
FEATURE_NEW_UI # Boolean feature flag
API_TIMEOUT_MS # Include units in name
✗ BAD:
db # Too short, unclear
Database-URL # Hyphens not standard
max retries # Spaces not allowed
timeout # Ambiguous (seconds? milliseconds?)Updating Variables
Changes to variables require a deployment restart to take effect:
- • Running deployments: Stop and start, or redeploy
- • New deployments: Will automatically use updated values
- • Inheritance changes: Affect all child levels automatically
Deleting Variables
Click the trash icon next to the variable. Be careful - this affects all deployments using this variable.
Managing Secrets
Adding Secrets
- 1. Navigate to the desired level
- 2. Go to the Secrets tab
- 3. Click Add Secret
- 4. Enter key and value (value will be masked as you type)
- 5. Click Save
⚠️ Once saved, you cannot view the secret value again. If you need to see it, you must delete and recreate it.
Updating Secrets
To update a secret value:
- 1. Delete the existing secret
- 2. Create a new secret with the same key and new value
- 3. Restart or redeploy applications using this secret
Secret Storage
Encryption:
- • Secrets encrypted at rest using AES-256
- • Stored separately from variables in secure vault
- • Decrypted only when injecting into containers
- • Never logged or displayed in UI/API
Best Practices
DO:
- ✓ Use secrets for ALL sensitive data (passwords, tokens, keys)
- ✓ Define common config at organization level (DRY principle)
- ✓ Override at lower levels only when necessary
- ✓ Use descriptive, consistent naming (DATABASE_URL, not DB or db_url)
- ✓ Document non-obvious variables (what they do, valid values)
- ✓ Include units in names when relevant (TIMEOUT_MS, MAX_SIZE_MB)
- ✓ Use environment-specific values (prod DB different from staging)
- ✓ Rotate secrets regularly (API keys, passwords)
- ✓ Delete unused variables to reduce clutter
DON'T:
- ✗ Store passwords or keys as variables (use secrets!)
- ✗ Hardcode values in application code - use variables
- ✗ Use production secrets in dev/staging environments
- ✗ Share secrets via email, Slack, or other insecure channels
- ✗ Commit secrets to git repositories
- ✗ Use generic names like TEMP, CONFIG, or DATA
- ✗ Override every variable at every level (defeats inheritance)
- ✗ Store large files as variables (use volumes or object storage)
Security Guidelines
Access Control
- • Only organization admins can manage organization-level variables/secrets
- • Environment maintainers can manage environment-level and below
- • Application owners can manage application and instance levels
- • Secrets are write-only - even admins cannot view after creation
Secret Rotation
Regularly rotate secrets to minimize risk:
- • Database passwords: Every 90 days
- • API keys: Every 180 days or on team changes
- • TLS certificates: Before expiration (Let's Encrypt auto-renews)
- • OAuth tokens: When user leaves team
Audit Trail
SeaGit logs all variable/secret changes:
- • Who created/updated/deleted
- • When the change occurred
- • Which deployments were affected
- • Previous and new values (variables only, not secrets)
Accessing in Applications
Variables and secrets are injected as environment variables. Access them using your language's standard methods:
Node.js
// Read environment variable
const dbUrl = process.env.DATABASE_URL;
const apiKey = process.env.API_KEY;
// With default fallback
const port = process.env.PORT || 3000;
// Parse numbers
const maxRetries = parseInt(process.env.MAX_RETRIES || '3', 10);
// Parse booleans
const debugMode = process.env.DEBUG === 'true';Python
import os
# Read environment variable
db_url = os.environ.get('DATABASE_URL')
api_key = os.environ['API_KEY'] # Raises KeyError if missing
# With default fallback
port = int(os.environ.get('PORT', 3000))
# Parse booleans
debug_mode = os.environ.get('DEBUG', 'false').lower() == 'true'Go
import (
"os"
"strconv"
)
// Read environment variable
dbURL := os.Getenv("DATABASE_URL")
// With default fallback
port := os.Getenv("PORT")
if port == "" {
port = "3000"
}
// Parse numbers
maxRetries, _ := strconv.Atoi(os.Getenv("MAX_RETRIES"))
// Parse booleans
debugMode := os.Getenv("DEBUG") == "true"Java
// Read environment variable
String dbUrl = System.getenv("DATABASE_URL");
// With default fallback
String port = System.getenv().getOrDefault("PORT", "3000");
// Parse numbers
int maxRetries = Integer.parseInt(
System.getenv().getOrDefault("MAX_RETRIES", "3")
);
// Parse booleans
boolean debugMode = "true".equals(System.getenv("DEBUG"));Advanced: Multi-Value Secrets
For complex configuration like JSON service account keys or multi-line certificates:
Option 1: Base64 Encoding (Recommended)
# Encode file to base64
cat service-account.json | base64 > encoded.txt
# Add as secret:
# Key: GCP_SERVICE_ACCOUNT_BASE64
# Value: (paste encoded content)
# In application, decode:
# Node.js
const decoded = Buffer.from(
process.env.GCP_SERVICE_ACCOUNT_BASE64,
'base64'
).toString('utf-8');
const config = JSON.parse(decoded);
# Python
import base64, json
decoded = base64.b64decode(os.environ['GCP_SERVICE_ACCOUNT_BASE64'])
config = json.loads(decoded)Option 2: Use Kubernetes Secrets (Advanced)
For very large files or complex structures, create Kubernetes secrets directly and mount as files:
# Create Kubernetes secret
kubectl create secret generic app-config \
--from-file=service-account.json \
--namespace=production
# Mount in pod (configure in application YAML)
volumes:
- name: config
secret:
secretName: app-config
volumeMounts:
- name: config
mountPath: /etc/config
readOnly: true
# Access in code
const config = require('/etc/config/service-account.json');API Reference
List Variables
GET /api/v1/organizations/{org_id}/variables
Response:
{
"variables": [
{
"key": "LOG_LEVEL",
"value": "info",
"level": "organization",
"created_at": "2024-01-15T10:00:00Z"
}
]
}Create Variable
POST /api/v1/environments/{env_id}/variables
{
"key": "DATABASE_HOST",
"value": "db.production.local"
}
Response: 201 CreatedCreate Secret
POST /api/v1/applications/{app_id}/secrets
{
"key": "API_KEY",
"value": "sk-1234567890abcdef"
}
Response: 201 Created
Note: value is write-only, cannot be retrievedGet Effective Configuration
Retrieve merged variables/secrets for a specific instance (with inheritance resolved):
GET /api/v1/instances/{instance_id}/config
Response:
{
"variables": {
"LOG_LEVEL": "error", # From instance (override)
"DATABASE_HOST": "db.local", # From environment
"MAX_CONNECTIONS": "100", # From organization
"APP_NAME": "api-service" # From application
},
"secrets": {
"API_KEY": "****", # Hidden
"DATABASE_PASSWORD": "****" # Hidden
}
}Troubleshooting
Variable not updating in application
Cause: Variables are injected at container start time
Solution: Restart deployment or redeploy after changing variables
Application crashes with "missing environment variable"
Cause: Required variable not set at any level
Solution: Add the variable at appropriate level, then redeploy
Can't remember secret value
Cause: Secrets are write-only by design
Solution: Generate/retrieve from source (e.g., AWS console for access keys), update secret
Variable showing wrong value in application
Cause: Overridden at a lower level (check inheritance)
Solution: Use "Get Effective Configuration" API to see final merged values