GitCaddy Vault
Encrypted Secrets Management for GitCaddy
GitCaddy Vault is a commercial module compiled directly into GitCaddy Server that provides enterprise-grade secrets management within your GitCaddy repositories. Store, version, and securely access credentials, API keys, certificates, and other sensitive data without leaving your Git workflow.
Features
Core Capabilities
- Encrypted Storage - All secrets encrypted at rest using AES-256-GCM with per-repository encryption keys
- Version History - Full version tracking with rollback capability for all secrets
- Audit Logging - Complete audit trail of all secret access and modifications
- CI/CD Tokens - Scoped tokens for secure automated access during builds and deployments
Secret Types
| Type | Description |
|---|---|
key-value |
Simple key-value pairs |
env-file |
Environment file format (KEY=value) |
file |
Arbitrary file content |
certificate |
TLS/SSL certificates |
ssh-key |
SSH private keys |
Security Architecture
┌─────────────────────────────────────────────────────────┐
│ GitCaddy Server │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Web UI │ │ REST API │ │ CI/CD API │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └──────────────────┼──────────────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Vault Service │ │
│ └────────┬────────┘ │
│ │ │
│ ┌──────────────────┼──────────────────┐ │
│ ┌─────▼─────┐ ┌──────▼──────┐ ┌──────▼──────┐ │
│ │ Crypto │ │ Models │ │ License │ │
│ │ Engine │ │ (XORM) │ │ Manager │ │
│ └───────────┘ └─────────────┘ └─────────────┘ │
│ │
│ (Compiled into GitCaddy Server) │
└─────────────────────────────────────────────────────────┘
Encryption Hierarchy:
- Master Key (KEK) - Server-level key encryption key
- Repository DEK - Per-repository data encryption key, encrypted by KEK
- Secret Values - Encrypted using repository DEK with AES-256-GCM
Installation
Requirements
- GitCaddy Server v1.0.0 or later (Vault is included automatically)
- Valid GitCaddy Vault license
Setup
GitCaddy Vault is compiled directly into GitCaddy Server - no separate installation required.
-
Add your license key via environment variable or file:
# Option 1: Environment variable export GITCADDY_LICENSE_KEY="<your-base64-license>" # Option 2: License file cp license.key /etc/gitcaddy/license.key -
Restart GitCaddy Server to activate the license
Configuration
Environment Variables
| Variable | Description | Default |
|---|---|---|
GITCADDY_LICENSE_KEY |
Base64-encoded license key | - |
GITCADDY_LICENSE_FILE |
Path to license file | /etc/gitcaddy/license.key |
GITCADDY_VAULT_KEK |
Master key encryption key (32 bytes, hex) | Auto-generated |
GITCADDY_DEV_MODE |
Skip license validation (dev only) | 0 |
License File Locations
GitCaddy Server searches for license files in this order:
- Path specified by
GITCADDY_LICENSE_FILE /etc/gitcaddy/license.key./custom/license.key./license.key
Usage
Web Interface
Access the Vault tab in any repository where you have admin permissions:
https://your-gitcaddy-instance/owner/repo/vault
Managing Secrets:
- Navigate to Repository > Vault > Secrets
- Click "New Secret" to create a secret
- Use dot notation for organization:
prod.database.password,staging.api.key
Creating CI/CD Tokens:
- Navigate to Repository > Vault > CI/CD Tokens
- Click "New Token"
- Define the scope (e.g.,
read:*,read:prod.*,write:db.credentials) - Set expiration time
- Copy the token immediately (shown only once)
REST API
All API endpoints require a valid vault token in the Authorization header.
Authentication:
# Bearer token format
Authorization: Bearer gvt_abc123...
# Or token format
Authorization: token gvt_abc123...
List Secrets:
curl -H "Authorization: Bearer $VAULT_TOKEN" \
https://gitcaddy.example.com/api/v1/repos/owner/repo/vault/secrets
Get Secret Value:
curl -H "Authorization: Bearer $VAULT_TOKEN" \
https://gitcaddy.example.com/api/v1/repos/owner/repo/vault/secrets/prod.database.password
Create Secret:
curl -X POST \
-H "Authorization: Bearer $VAULT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "prod.database.password",
"description": "Production database credentials",
"type": "key-value",
"value": "secret-password-here"
}' \
https://gitcaddy.example.com/api/v1/repos/owner/repo/vault/secrets
Update Secret:
curl -X PUT \
-H "Authorization: Bearer $VAULT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "key-value",
"value": "new-secret-value",
"comment": "Rotated credentials"
}' \
https://gitcaddy.example.com/api/v1/repos/owner/repo/vault/secrets/prod.database.password
Delete Secret:
curl -X DELETE \
-H "Authorization: Bearer $VAULT_TOKEN" \
https://gitcaddy.example.com/api/v1/repos/owner/repo/vault/secrets/prod.database.password
CI/CD Integration
GitHub Actions / Gitea Actions:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Fetch secrets
run: |
DB_PASSWORD=$(curl -s -H "Authorization: Bearer ${{ secrets.VAULT_TOKEN }}" \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/vault/secrets/prod.database.password" \
| jq -r '.value')
echo "::add-mask::$DB_PASSWORD"
echo "DB_PASSWORD=$DB_PASSWORD" >> $GITHUB_ENV
GitLab CI:
deploy:
script:
- |
export DB_PASSWORD=$(curl -s -H "Authorization: Bearer $VAULT_TOKEN" \
"$CI_SERVER_URL/api/v1/repos/$CI_PROJECT_PATH/vault/secrets/prod.database.password" \
| jq -r '.value')
Token Scopes
Token scopes control access to secrets using a simple grammar:
| Scope | Description |
|---|---|
read:* |
Read access to all secrets |
write:* |
Read and write access to all secrets |
read:prod.* |
Read access to secrets starting with prod. |
write:db.credentials |
Write access to specific secret db.credentials |
admin |
Full administrative access |
Multiple scopes: Separate with commas: read:prod.*,write:staging.*
License Tiers
| Feature | Solo | Pro | Team | Enterprise |
|---|---|---|---|---|
| Users | 1 | 5 | 25 | Unlimited |
| Secrets per repo | 5 | Unlimited | Unlimited | Unlimited |
| Audit retention | 7 days | 90 days | 1 year | Custom |
| Version history | No | Yes | Yes | Yes |
| CI/CD tokens | No | Yes | Yes | Yes |
| SSO integration | No | No | Yes | Yes |
| Priority support | No | No | No | Yes |
Database Schema
GitCaddy Vault uses the following tables:
vault_secret- Secret metadatavault_secret_version- Versioned secret values (encrypted)vault_repo_key- Per-repository encryption keysvault_token- CI/CD access tokensvault_audit_entry- Audit log entries
Development
Architecture: Vault ↔ Server Sync
Important for contributors and AI assistants:
GitCaddy Vault is the source of truth for vault-related templates and locales. Due to Go plugin compilation limitations, vault code is compiled directly into GitCaddy Server rather than loaded as a dynamic plugin.
┌─────────────────────────────────────────────────────────────────┐
│ gitcaddy-vault (this repo) │
│ SOURCE OF TRUTH for: │
│ • templates/repo/vault/*.tmpl → UI templates │
│ • locale/*.json → Translation strings │
│ • models/, services/, crypto/ → Business logic │
│ • license/ → License validation │
└─────────────────────────┬───────────────────────────────────────┘
│
BUILD TIME SYNC
(scripts/sync-vault.sh)
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ gitcaddy-server │
│ RECEIVES from vault: │
│ • templates/repo/vault/*.tmpl ← Copied from vault │
│ • options/locale/*.json ← vault.* keys merged │
│ │
│ SERVER-ONLY (not in vault): │
│ • templates/repo/vault/feature_upgrade.tmpl │
│ • templates/repo/vault/not_installed.tmpl │
│ • templates/repo/vault/upgrade.tmpl │
│ • routers/web/repo/vault/vault.go ← Router glue code │
│ • services/vault/vault.go ← Service wrappers │
└─────────────────────────────────────────────────────────────────┘
When making changes:
- Edit templates/locales in gitcaddy-vault (this repo)
- The CI build automatically syncs to gitcaddy-server
- Server-specific templates (upgrade prompts) stay in server repo
- Go router code stays in server repo (thin integration layer)
Why not Go plugins? Go plugins require exact compiler version and dependency matches between plugin and host. This is fragile in practice, so we compile vault directly into the server binary.
Building
The Vault module is compiled directly into GitCaddy Server. To build the server with Vault:
# Clone GitCaddy Server (includes Vault)
git clone https://git.marketally.com/gitcaddy/server.git
cd server
# Build the server (Vault is included automatically)
make build
# Run tests
go test ./...
Keygen Utility
The license key generation tool is built separately:
# Clone the vault repository
git clone https://git.marketally.com/gitcaddy/vault.git
cd vault
# Build the keygen utility
go build -o keygen ./cmd/keygen
Generating License Keys
# Generate a new keypair (do this once, keep private key secure!)
go run ./cmd/keygen -generate-keys
# Sign a license
go run ./cmd/keygen -sign \
-email customer@example.com \
-tier pro \
-duration 365d \
-private-key /secure/path/private.key
Local Development
Set GITCADDY_DEV_MODE=1 to skip license validation during development:
export GITCADDY_DEV_MODE=1
Key Management
Master Key Configuration
The vault uses a master Key Encryption Key (KEK) to encrypt repository-level Data Encryption Keys (DEKs). Configure the master key using one of these methods (in priority order):
-
app.ini (recommended for production):
[vault] MASTER_KEY = <64-character-hex-string> -
Environment variable:
export GITCADDY_VAULT_KEY="<64-character-hex-string>" -
Key file:
export GITCADDY_VAULT_KEY_FILE="/etc/gitcaddy/vault.key" -
Fallback (not recommended): If none of the above are set, Gitea's
SECRET_KEYis used as a fallback.
Generate a secure master key:
openssl rand -hex 32
Key Migration
If you change your master key or need to migrate from the fallback key to a dedicated master key, use the Key Migration feature.
When to use key migration:
- You changed the
MASTER_KEYin app.ini and existing secrets are now inaccessible - Secrets were created using the fallback key before a dedicated master key was configured
- You see "Encryption Key Mismatch" errors when accessing vault secrets
Web UI:
- Navigate to Repository > Vault > Key Migration (admin only)
- Enter the old master key (the previous
MASTER_KEYor Gitea'sSECRET_KEY) - Choose the migration scope (this repository or all repositories)
- Click "Start Migration"
API:
curl -X POST \
-H "Authorization: Bearer $VAULT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"old_key": "<previous-master-key-or-secret-key>",
"repo_id": 0
}' \
https://gitcaddy.example.com/owner/repo/-/vault/api/migrate-key
The old_key can be:
- A 64-character hex string (will be decoded to 32 bytes)
- Raw text (will be used as-is, padded/truncated to 32 bytes)
Set repo_id to 0 to migrate the current repository, or specify a repo ID for a specific repository. Instance admins can migrate all repositories at once.
DEK Rotation (Enterprise)
For enhanced security, Enterprise license holders can rotate the Data Encryption Key (DEK) for a repository. This generates a new DEK and re-encrypts all secret versions.
Web UI:
- Navigate to Repository > Vault > Key Migration
- Click "Rotate DEK" in the DEK Rotation section
API:
curl -X POST \
-H "Authorization: Bearer $VAULT_TOKEN" \
https://gitcaddy.example.com/owner/repo/-/vault/api/rotate-key
Security Considerations
- Key Management - The master KEK should be stored securely (HSM, KMS, or secure environment variable)
- Token Storage - Vault tokens are hashed with SHA-256 before storage
- Audit Trail - All access is logged with IP address and user information
- Soft Delete - Deleted secrets are retained for recovery before permanent deletion
- TLS Required - Always use HTTPS in production
Support
- Documentation: https://docs.gitcaddy.com/vault
- Issues: https://git.marketally.com/gitcaddy/vault/issues
- Email: support@marketally.com
License
Business Source License 1.1 - See LICENSE file for details.
Copyright 2026 MarketAlly. All rights reserved.