2
0

3 Commits

Author SHA1 Message Date
b824b8e3be feat(vault): add database migration system for vault plugin
All checks were successful
Build and Release / Tests (push) Successful in 1m40s
Build and Release / Lint (push) Successful in 1m41s
Build and Release / Create Release (push) Successful in 0s
Implements explicit column migration logic to handle schema upgrades that xorm.Sync() doesn't reliably perform. Adds encryption_mode column migration for vault_secret table to support lockbox (E2E) encryption. Includes database-agnostic column existence checks and ALTER TABLE statement generation for PostgreSQL, MySQL, SQLite, and MSSQL.
2026-02-08 10:38:39 -05:00
e9b109c464 docs: add comprehensive API reference documentation
All checks were successful
Build and Release / Tests (push) Successful in 1m26s
Build and Release / Lint (push) Successful in 1m59s
Build and Release / Create Release (push) Has been skipped
Creates new API.md with complete API reference including:
- Architecture and encryption model documentation
- Installation and configuration guides
- Authentication methods and API endpoints
- Go package API documentation
- Error codes and code examples
- License tier information

Also updates README.md to reference the new API documentation.
2026-02-07 09:25:38 -05:00
69f71cef7e docs: add lockbox end-to-end encryption documentation
Documents the new Lockbox feature for client-side encryption including:
- Feature comparison table between standard and lockbox modes
- Encryption scheme details (Argon2id + AES-256-GCM)
- API usage examples for Go, TypeScript, and Python SDKs
- Raw API endpoint documentation for manual encryption
2026-02-07 09:19:32 -05:00
4 changed files with 1923 additions and 3 deletions

View File

@@ -1,7 +1,7 @@
{
"id": "note-1768964774362-3y9dna8sm",
"title": "working",
"content": "● Done. Updated all vault locale files with the new translation keys.\n\n Summary of what's now in gitcaddy-vault:\n 1. Templates: list.tmpl, view.tmpl, audit.tmpl, tokens.tmpl (copied)\n 2. Locale files: Added 20+ new translation keys to all locale files\n\n What stays in gitcaddy-server:\n - Router handlers (routers/web/repo/vault/vault.go) - these call the vault service\n - Service interface definitions (services/vault/vault.go)\n - Locale files also need the keys (for ctx.Locale.Tr calls in templates)\n\n```\n Now you need to:\n 1. Commit and tag vault as v1.0.32\n 2. Update server go.mod to v1.0.32\n 3. Release server v3.1.10\n 4. Deploy to git-test\n```\n\n● So:\n\n | Key | Value |\n |---------------------------|------------------------------------------------------------------|\n | Old (SECRET_KEY fallback) | bfe6e73282adba6545c2c9ba3542a332a80d62d1644b3b24c14b020a838c136f |\n | New (MASTER_KEY) | af1a7e9d7fe73258800b434ab5ffe7bf1ee3dff49740ef3cc3c5c014587acc08 |\n",
"content": "● Done. Updated all vault locale files with the new translation keys.\n\n Summary of what's now in gitcaddy-vault:\n 1. Templates: list.tmpl, view.tmpl, audit.tmpl, tokens.tmpl (copied)\n 2. Locale files: Added 20+ new translation keys to all locale files\n\n What stays in gitcaddy-server:\n - Router handlers (routers/web/repo/vault/vault.go) - these call the vault service\n - Service interface definitions (services/vault/vault.go)\n - Locale files also need the keys (for ctx.Locale.Tr calls in templates)\n\n```\n Now you need to:\n 1. Commit and tag vault as v1.0.32\n 2. Update server go.mod to v1.0.32\n 3. Release server v3.1.10\n 4. Deploy to git-test\n```\n",
"createdAt": 1768964774360,
"updatedAt": 1770432337981
"updatedAt": 1770512254003
}

1803
API.md Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,30 @@ GitCaddy Vault is a commercial module compiled directly into GitCaddy Server tha
| `certificate` | TLS/SSL certificates |
| `ssh-key` | SSH private keys |
### Lockbox (End-to-End Encryption)
Lockbox provides optional client-side encryption where the server **never sees your plaintext secrets**. Perfect for highly sensitive data where you don't want even the server administrator to have access.
| Feature | Standard Mode | Lockbox Mode |
|---------|--------------|--------------|
| Server sees plaintext | Yes | No |
| Passphrase required | No | Yes |
| Recovery if passphrase lost | Yes | No |
| Web UI viewing | Yes | CLI/SDK only |
**How Lockbox Works:**
1. Client encrypts secret with your passphrase using Argon2id + AES-256-GCM
2. Encrypted blob is sent to server in `lockbox:v1:...` format
3. Server wraps the blob with repository DEK (double encryption)
4. On retrieval, server unwraps DEK layer, returns lockbox blob
5. Client decrypts with your passphrase
**Encryption Scheme:**
- Key derivation: Argon2id (time=1, memory=64MB, parallelism=4)
- Cipher: AES-256-GCM with 12-byte nonce
- Salt: 16 bytes random per secret
- Format: `lockbox:v1:<base64(salt)>:<base64(nonce||ciphertext||tag)>`
### Security Architecture
```

View File

@@ -5,6 +5,7 @@ package vault
import (
"context"
"fmt"
"git.marketally.com/gitcaddy/gitcaddy-vault/crypto"
"git.marketally.com/gitcaddy/gitcaddy-vault/license"
@@ -98,7 +99,99 @@ func (p *VaultPlugin) RegisterModels() []any {
// Migrate runs database migrations for this plugin
func (p *VaultPlugin) Migrate(ctx context.Context, x *xorm.Engine) error {
return x.Sync(p.RegisterModels()...)
// First, sync tables (works for fresh installations)
if err := x.Sync(p.RegisterModels()...); err != nil {
return err
}
// Then run explicit column migrations for upgrades
// xorm.Sync() doesn't reliably add columns to existing tables
return runColumnMigrations(ctx, x)
}
// runColumnMigrations adds any missing columns to existing tables.
// This handles the case where xorm.Sync() fails to add new columns.
func runColumnMigrations(ctx context.Context, x *xorm.Engine) error {
migrations := []struct {
table string
column string
columnType string
defaultVal string
}{
// v1.0.31: Add encryption_mode column for lockbox (E2E) secrets
{"vault_secret", "encryption_mode", "VARCHAR(20)", "'standard'"},
}
for _, m := range migrations {
exists, err := columnExists(x, m.table, m.column)
if err != nil {
return err
}
if !exists {
log.Info("Vault migration: adding column %s.%s", m.table, m.column)
sql := buildAddColumnSQL(x, m.table, m.column, m.columnType, m.defaultVal)
if _, err := x.Exec(sql); err != nil {
return fmt.Errorf("failed to add column %s.%s: %w", m.table, m.column, err)
}
}
}
return nil
}
// columnExists checks if a column exists in a table
func columnExists(x *xorm.Engine, table, column string) (bool, error) {
dialect := x.Dialect().URI().DBType
var sql string
switch dialect {
case "postgres":
sql = fmt.Sprintf(`SELECT 1 FROM information_schema.columns WHERE table_name = '%s' AND column_name = '%s'`, table, column)
case "mysql":
sql = fmt.Sprintf(`SELECT 1 FROM information_schema.columns WHERE table_name = '%s' AND column_name = '%s'`, table, column)
case "sqlite3":
// SQLite uses PRAGMA
results, err := x.Query(fmt.Sprintf("PRAGMA table_info(%s)", table))
if err != nil {
return false, err
}
for _, row := range results {
if string(row["name"]) == column {
return true, nil
}
}
return false, nil
case "mssql":
sql = fmt.Sprintf(`SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('%s') AND name = '%s'`, table, column)
default:
// Fallback: try postgres-style
sql = fmt.Sprintf(`SELECT 1 FROM information_schema.columns WHERE table_name = '%s' AND column_name = '%s'`, table, column)
}
results, err := x.Query(sql)
if err != nil {
return false, err
}
return len(results) > 0, nil
}
// buildAddColumnSQL builds the appropriate ALTER TABLE statement for the database type
func buildAddColumnSQL(x *xorm.Engine, table, column, columnType, defaultVal string) string {
dialect := x.Dialect().URI().DBType
switch dialect {
case "postgres":
return fmt.Sprintf(`ALTER TABLE "%s" ADD COLUMN IF NOT EXISTS "%s" %s NOT NULL DEFAULT %s`, table, column, columnType, defaultVal)
case "mysql":
// MySQL doesn't have IF NOT EXISTS for columns, but we already checked
return fmt.Sprintf("ALTER TABLE `%s` ADD COLUMN `%s` %s NOT NULL DEFAULT %s", table, column, columnType, defaultVal)
case "sqlite3":
return fmt.Sprintf(`ALTER TABLE "%s" ADD COLUMN "%s" %s NOT NULL DEFAULT %s`, table, column, columnType, defaultVal)
case "mssql":
return fmt.Sprintf(`ALTER TABLE [%s] ADD [%s] %s NOT NULL DEFAULT %s`, table, column, columnType, defaultVal)
default:
// Fallback to standard SQL
return fmt.Sprintf(`ALTER TABLE "%s" ADD COLUMN "%s" %s NOT NULL DEFAULT %s`, table, column, columnType, defaultVal)
}
}
// RegisterRepoWebRoutes adds vault routes under /{owner}/{repo}/vault