233 lines
8.8 KiB
Markdown
233 lines
8.8 KiB
Markdown
# VAULT MIGRATION PLAN - Multi-Tenant Structure
|
|
|
|
## Hierarchy
|
|
```
|
|
tenant (cluster/deployment) ← INFRASTRUCTURE
|
|
└── org (customer organization)
|
|
├── bot
|
|
└── user
|
|
```
|
|
|
|
**tenant ≠ org**
|
|
- **tenant** = deployment cluster (dev, staging, prod)
|
|
- **org** = customer organization inside a tenant
|
|
|
|
---
|
|
|
|
## VAULT PATH STRUCTURE
|
|
|
|
```
|
|
gbo/
|
|
├── tenants/ # PER-TENANT (cluster/deployment)
|
|
│ └── {tenant_id}/ # dev, staging, prod
|
|
│ ├── infrastructure/ # TENANT INFRASTRUCTURE
|
|
│ │ ├── tables/ # host, port, username, password
|
|
│ │ ├── drive/ # host, port, accesskey, secret
|
|
│ │ ├── cache/ # host, port, password
|
|
│ │ ├── email/ # smtp host, port, user, pass
|
|
│ │ ├── directory/ # Zitadel url
|
|
│ │ ├── llm/ # LLM endpoint
|
|
│ │ └── models/ # Model server url
|
|
│ │
|
|
│ └── config/ # Tenant settings
|
|
│ ├── name
|
|
│ ├── domain
|
|
│ └── settings
|
|
│
|
|
├── orgs/ # PER-ORGANIZATION (customer)
|
|
│ └── {org_id}/
|
|
│ ├── bots/
|
|
│ │ └── {bot_id}/
|
|
│ │ ├── email/ # Bot email credentials
|
|
│ │ ├── whatsapp/
|
|
│ │ ├── llm/ # Bot-specific LLM override
|
|
│ │ └── api-keys/
|
|
│ │
|
|
│ └── users/
|
|
│ └── {user_id}/
|
|
│ ├── email/ # User email credentials
|
|
│ └── oauth/
|
|
│
|
|
└── system/ # GLOBAL FALLBACK
|
|
├── jwt/secret
|
|
├── tables/ # Fallback if tenant not set
|
|
├── drive/
|
|
├── cache/
|
|
├── email/
|
|
├── llm/
|
|
├── directory/
|
|
├── security/
|
|
├── alm/
|
|
├── cloud/
|
|
└── app/
|
|
│ │ │ │ │ ├── smtp-port
|
|
│ │ │ │ │ ├── smtp-user
|
|
│ │ │ │ │ ├── smtp-password
|
|
│ │ │ │ │ ├── imap-host
|
|
│ │ │ │ │ ├── imap-port
|
|
│ │ │ │ │ ├── imap-user
|
|
│ │ │ │ │ └── imap-password
|
|
│ │ │ │ │
|
|
│ │ │ │ ├── whatsapp/ # Bot WhatsApp
|
|
│ │ │ │ │ ├── phone-number-id
|
|
│ │ │ │ │ ├── business-account-id
|
|
│ │ │ │ │ └── api-key
|
|
│ │ │ │ │
|
|
│ │ │ │ ├── llm/ # Bot-specific LLM (override)
|
|
│ │ │ │ │ ├── provider
|
|
│ │ │ │ │ ├── model
|
|
│ │ │ │ │ └── api-key
|
|
│ │ │ │ │
|
|
│ │ │ │ └── api-keys/
|
|
│ │ │ │ ├── openai
|
|
│ │ │ │ ├── anthropic
|
|
│ │ │ │ └── custom/
|
|
│ │ │ │
|
|
│ │ │ └── {bot_id2}/
|
|
│ │ │ └── ...
|
|
│ │ │
|
|
│ │ └── users/
|
|
│ │ ├── {user_id}/
|
|
│ │ │ ├── email/ # User email credentials
|
|
│ │ │ │ ├── imap-host
|
|
│ │ │ │ ├── imap-port
|
|
│ │ │ │ ├── imap-user
|
|
│ │ │ │ ├── imap-password
|
|
│ │ │ │ ├── smtp-host
|
|
│ │ │ │ ├── smtp-port
|
|
│ │ │ │ ├── smtp-user
|
|
│ │ │ │ └── smtp-password
|
|
│ │ │ │
|
|
│ │ │ └── oauth/
|
|
│ │ │ ├── google/
|
|
│ │ │ │ ├── client-id
|
|
│ │ │ │ └── client-secret
|
|
│ │ │ ├── microsoft/
|
|
│ │ │ │ ├── client-id
|
|
│ │ │ │ └── client-secret
|
|
│ │ │ └── github/
|
|
│ │ │ ├── client-id
|
|
│ │ │ └── client-secret
|
|
│ │ │
|
|
│ │ └── {user_id2}/
|
|
│ │ └── ...
|
|
│ │
|
|
│ └── {org_id2}/
|
|
│ └── ...
|
|
│
|
|
├── system/ # SYSTEM-WIDE (includes ALM/deployment)
|
|
│ ├── jwt/
|
|
│ │ └── secret
|
|
│ ├── tables/ # Database
|
|
│ │ ├── host
|
|
│ │ ├── port
|
|
│ │ ├── database
|
|
│ │ ├── username
|
|
│ │ └── password
|
|
│ ├── drive/ # Storage
|
|
│ │ ├── accesskey
|
|
│ │ └── secret
|
|
│ ├── cache/
|
|
│ │ └── url
|
|
│ ├── email/ # Global SMTP fallback
|
|
│ │ ├── smtp-host
|
|
│ │ ├── smtp-port
|
|
│ │ ├── smtp-user
|
|
│ │ ├── smtp-password
|
|
│ │ └── smtp-from
|
|
│ ├── llm/ # Global LLM defaults
|
|
│ │ ├── url
|
|
│ │ ├── model
|
|
│ │ └── providers/
|
|
│ │ └── openai/
|
|
│ │ └── api-key
|
|
│ ├── models/ # Model serving
|
|
│ │ └── url
|
|
│ ├── directory/ # Zitadel
|
|
│ │ └── config
|
|
│ ├── security/
|
|
│ │ ├── require-auth
|
|
│ │ └── anonymous-paths
|
|
│ ├── alm/ # ALM/Deployment (Forgejo)
|
|
│ │ ├── url
|
|
│ │ ├── token
|
|
│ │ └── default-org
|
|
│ ├── cloud/ # Cloud providers (AWS, etc)
|
|
│ │ ├── access-key
|
|
│ │ ├── secret-key
|
|
│ │ ├── region
|
|
│ │ └── s3-endpoint
|
|
│ └── app/ # Application config
|
|
│ ├── url
|
|
│ ├── environment
|
|
│ └── disable-tls
|
|
```
|
|
|
|
---
|
|
|
|
## ENV VARS → VAULT MAPPING
|
|
|
|
| Current ENV | Vault Path | Scope |
|
|
|------------|------------|-------|
|
|
| `JWT_SECRET` | `gbo/system/jwt/secret` | system |
|
|
| `LLM_KEY` | `gbo/system/llm/providers/openai/api-key` | system |
|
|
| `OPENAI_API_KEY` | `gbo/system/llm/providers/openai/api-key` | system |
|
|
| `SMTP_HOST` | `gbo/system/email/smtp-host` | system |
|
|
| `SMTP_USER` | `gbo/system/email/smtp-user` | system |
|
|
| `SMTP_PASS` | `gbo/system/email/smtp-password` | system |
|
|
| `SMTP_FROM` | `gbo/system/email/smtp-from` | system |
|
|
| `FORGEJO_URL` | `gbo/system/alm/url` | system |
|
|
| `FORGEJO_TOKEN` | `gbo/system/alm/token` | system |
|
|
| `FORGEJO_DEFAULT_ORG` | `gbo/system/alm/default-org` | system |
|
|
| `AWS_ACCESS_KEY_ID` | `gbo/system/cloud/access-key` | system |
|
|
| `AWS_SECRET_ACCESS_KEY` | `gbo/system/cloud/secret-key` | system |
|
|
| `AWS_REGION` | `gbo/system/cloud/region` | system |
|
|
| `S3_ENDPOINT` | `gbo/system/cloud/s3-endpoint` | system |
|
|
| `ZITADEL_ISSUER_URL` | `gbo/system/directory/zitadel-issuer` | system |
|
|
| `ZITADEL_CLIENT_ID` | `gbo/system/directory/zitadel-client-id` | system |
|
|
| `REQUIRE_AUTH` | `gbo/system/security/require-auth` | system |
|
|
| `ANONYMOUS_PATHS` | `gbo/system/security/anonymous-paths` | system |
|
|
| `APP_URL` | `gbo/system/app/url` | system |
|
|
| `BOTSERVER_ENV` | `gbo/system/app/environment` | system |
|
|
| `LLM_URL` | `gbo/system/llm/url` | system |
|
|
| `LLM_MODEL` | `gbo/system/llm/model` | system |
|
|
| `BOTMODELS_URL` | `gbo/system/models/url` | system |
|
|
|
|
---
|
|
|
|
## CODE PATTERNS
|
|
|
|
### Get Bot Email (tenant → bot)
|
|
```rust
|
|
async fn get_bot_email(state: &AppState, org_id: Uuid, bot_id: Uuid) -> Result<BotEmail> {
|
|
// Try per-bot Vault path first
|
|
let path = format!("gbo/tenants/{}/bots/{}/email", org_id, bot_id);
|
|
if let Ok(creds) = state.secrets.get_secret(&path).await {
|
|
return Ok(BotEmail::from_vault(creds));
|
|
}
|
|
// Fallback: system SMTP
|
|
let system = state.secrets.get_secret("gbo/system/email").await?;
|
|
Ok(BotEmail::from_system(system))
|
|
}
|
|
```
|
|
|
|
### Get User Email (tenant → user)
|
|
```rust
|
|
async fn get_user_email(state: &AppState, org_id: Uuid, user_id: Uuid) -> Result<UserEmail> {
|
|
let path = format!("gbo/tenants/{}/users/{}/email", org_id, user_id);
|
|
state.secrets.get_secret(&path).await
|
|
}
|
|
```
|
|
|
|
### Get Bot LLM (tenant → bot, with system fallback)
|
|
```rust
|
|
async fn get_bot_llm(state: &AppState, org_id: Uuid, bot_id: Uuid) -> Result<LlmConfig> {
|
|
// Bot-specific override
|
|
let bot_path = format!("gbo/tenants/{}/bots/{}/llm", org_id, bot_id);
|
|
if let Ok(config) = state.secrets.get_secret(&bot_path).await {
|
|
return Ok(LlmConfig::from_vault(config));
|
|
}
|
|
// System default
|
|
state.secrets.get_secret("gbo/system/llm").await
|
|
}
|
|
```
|