ADR-065: Credentials and Secrets Management

Status

Proposed

Date

2025-12-19

Deciders

Context and Problem Statement

iDempiere Hub needs secure access to various external services:

Currently, credentials are managed via environment variables, which works for development but has limitations:

  1. No rotation: Credentials are static
  2. No audit: No tracking of credential access
  3. No centralization: Each deployment manages credentials independently
  4. Limited security: Environment variables can leak in logs

Decision Drivers

Considered Options

  1. Environment Variables Only - Current approach
  2. AWS Secrets Manager - AWS-native secret management
  3. HashiCorp Vault - Platform-agnostic secret management
  4. Quarkus Vault Extension - Built-in Vault support
  5. Hybrid Approach - Multiple providers with fallback chain

Decision Outcome

Chosen option: "Hybrid Approach" with provider abstraction

┌─────────────────────────────────────────────────────────────────────────┐
│  CredentialProvider Interface                                            │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  interface CredentialProvider {                                          │
│    Optional<String> getSecret(String key);                              │
│    boolean isAvailable();                                                │
│    int priority();  // Higher = preferred                                │
│  }                                                                        │
│                                                                          │
└───────────────────────────────────┬─────────────────────────────────────┘
                                    │
         ┌──────────────────────────┼──────────────────────────┐
         │                          │                          │
    ┌────▼────┐              ┌──────▼──────┐            ┌──────▼──────┐
    │ AWS     │              │ HashiCorp   │            │ Environment │
    │ Secrets │              │ Vault       │            │ Variables   │
    │ Manager │              │             │            │ (fallback)  │
    │ (100)   │              │ (90)        │            │ (10)        │
    └─────────┘              └─────────────┘            └─────────────┘

Credential Categories

Category Examples Provider Recommendation
VCS Tokens GITHUB_TOKEN, GITLAB_TOKEN AWS Secrets Manager
API Keys ANTHROPIC_API_KEY, OPENAI_API_KEY AWS Secrets Manager
OAuth2 iDempiere client credentials Vault or AWS
Database IDEMPIERE_DB_PASSWORD Environment (local) / Vault (prod)
Cloud AWS_ACCESS_KEY_ID IAM roles (no credentials)

Proposed Configuration

# Provider selection (auto, aws, vault, environment)
idempiere.hub.credentials.provider=${CREDENTIAL_PROVIDER:auto}

# AWS Secrets Manager
idempiere.hub.credentials.aws.enabled=true
idempiere.hub.credentials.aws.region=${AWS_REGION:us-east-1}
idempiere.hub.credentials.aws.prefix=idempiere-hub/

# HashiCorp Vault
idempiere.hub.credentials.vault.enabled=false
idempiere.hub.credentials.vault.address=${VAULT_ADDR:}
idempiere.hub.credentials.vault.token=${VAULT_TOKEN:}
idempiere.hub.credentials.vault.path=secret/data/idempiere-hub

# Environment (always available as fallback)
idempiere.hub.credentials.environment.enabled=true

Secret Naming Convention

AWS Secrets Manager:
  idempiere-hub/github-token
  idempiere-hub/anthropic-api-key
  idempiere-hub/oauth2/client-credentials

HashiCorp Vault:
  secret/data/idempiere-hub/github-token
  secret/data/idempiere-hub/anthropic-api-key

Environment Variables:
  GITHUB_TOKEN
  ANTHROPIC_API_KEY
  OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET

GitHub Integration Flow

Scenario: Generate Plugin and Push to GitHub

┌─────────────────────────────────────────────────────────────────────────────┐
│  User/n8n: "Generate plugin org.idempiere.rating and push to GitHub"        │
└───────────────────────────────────┬─────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│  1. GENERATE PHASE (ADR-064)                                                │
│                                                                             │
│  GeneratorToolLogic.generatePlugin(artifactId="org.idempiere.rating")       │
│       │                                                                     │
│       ▼                                                                     │
│  GeneratorConfig.resolveOutputDir()                                         │
│       │                                                                     │
│       ▼                                                                     │
│  GITHUB_ROOT=/Users/dev/github                                              │
│  Output: /Users/dev/github/org.idempiere.rating/                            │
│       │                                                                     │
│       ▼                                                                     │
│  Files Written:                                                             │
│    ├── pom.xml                                                              │
│    ├── src/main/java/...                                                    │
│    └── META-INF/MANIFEST.MF                                                 │
└───────────────────────────────────┬─────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│  2. GIT INIT & PUSH PHASE (Uses Credentials)                                │
│                                                                             │
│  GitService.initAndPush(repoPath, remoteUrl)                                │
│       │                                                                     │
│       ▼                                                                     │
│  CredentialService.getSecret("github-token")                                │
│       │                                                                     │
│       ├─── AWS Secrets Manager ────────────────────────────────────┐        │
│       │    GET idempiere-hub/github-token                          │        │
│       │         │                                                  │        │
│       │         ▼                                                  │        │
│       │    Returns: "ghp_xxxxxxxxxxxx"                             │        │
│       │                                                            │        │
│       └─── OR Environment Variable ────────────────────────────────┤        │
│            $GITHUB_TOKEN = "ghp_xxxxxxxxxxxx"                      │        │
│                                                                    │        │
│       ◄────────────────────────────────────────────────────────────┘        │
│       │                                                                     │
│       ▼                                                                     │
│  git init                                                                   │
│  git remote add origin https://github.com/cloudempiere/org.idempiere.rating │
│  git add .                                                                  │
│  git commit -m "Initial plugin structure"                                   │
│       │                                                                     │
│       ▼                                                                     │
│  git push (with token authentication)                                       │
│    URL: https://{GITHUB_TOKEN}@github.com/cloudempiere/org.idempiere.rating │
│                                                                             │
└───────────────────────────────────┬─────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│  3. RESULT                                                                  │
│                                                                             │
│  ✅ Plugin generated at: /Users/dev/github/org.idempiere.rating/            │
│  ✅ Repository created: https://github.com/cloudempiere/org.idempiere.rating│
│  ✅ Initial commit pushed                                                   │
└─────────────────────────────────────────────────────────────────────────────┘

AWS Secrets Manager Setup

┌─────────────────────────────────────────────────────────────────────────────┐
│  AWS Secrets Manager                                                        │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  Secret: idempiere-hub/github-token                                         │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  {                                                                   │   │
│  │    "token": "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",              │   │
│  │    "username": "cloudempiere-bot",                                   │   │
│  │    "org": "cloudempiere"                                             │   │
│  │  }                                                                   │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  Secret: idempiere-hub/github-app (Alternative: GitHub App)                 │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  {                                                                   │   │
│  │    "app_id": "123456",                                               │   │
│  │    "installation_id": "789012",                                      │   │
│  │    "private_key": "-----BEGIN RSA PRIVATE KEY-----..."              │   │
│  │  }                                                                   │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Configuration Examples

n8n Server (Production)

{
  "mcpServers": {
    "idempiere": {
      "env": {
        "GITHUB_ROOT": "/opt/n8n/workspaces/plugins",
        "CREDENTIAL_PROVIDER": "aws",
        "AWS_REGION": "us-east-1"
      }
    }
  }
}

n8n → iDempiere Hub → AWS Secrets Manager → GitHub

Developer Workstation (Simple)

# ~/.zshrc
export GITHUB_ROOT="$HOME/github"
export GITHUB_TOKEN="ghp_xxx"  # Personal access token
# No AWS needed - uses environment variable

GitService Interface (Proposed)

public interface GitService {

    /**
     * Initialize repo and push to remote.
     * Credentials resolved automatically via CredentialService.
     */
    GitResult initAndPush(Path repoPath, String remoteUrl, String commitMessage);

    /**
     * Create GitHub repository via API.
     */
    GitResult createRepository(String org, String repoName, boolean isPrivate);

    /**
     * Clone existing repository.
     */
    GitResult clone(String remoteUrl, Path targetPath);
}

CredentialService Interface (Proposed)

public interface CredentialService {

    /**
     * Get secret by key. Tries providers in priority order.
     */
    Optional<String> getSecret(String key);

    /**
     * Get structured secret (JSON parsed).
     */
    <T> Optional<T> getSecret(String key, Class<T> type);

    /**
     * Check if a secret exists.
     */
    boolean hasSecret(String key);
}

Usage Examples

n8n Workflow (AWS)

{
  "env": {
    "CREDENTIAL_PROVIDER": "aws",
    "AWS_REGION": "us-east-1"
  }
}

CI/CD (GitHub Actions)

env:
  CREDENTIAL_PROVIDER: environment
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

Local Development

# ~/.zshrc - simple environment variables
export GITHUB_TOKEN="ghp_xxx"
export ANTHROPIC_API_KEY="sk-ant-xxx"

# No CREDENTIAL_PROVIDER needed - defaults to environment

Implementation Plan

Phase 1: Interface & Environment Provider

Phase 2: AWS Secrets Manager

Phase 3: HashiCorp Vault (Optional)

Phase 4: Credential Rotation

References


Note: This ADR is proposed. Implementation priority depends on security requirements and deployment model (dev vs production).

Path: /docs/developers/architecture/idempiere-hub/065-credentials-secrets-management