Component Architecture
This page provides a detailed look at each component in the Voidkey system and how they interact.
broker-core
Section titled “broker-core”The TypeScript core library that implements the fundamental credential brokering logic.
Key Responsibilities
Section titled “Key Responsibilities”- Token Validation: Validates OIDC tokens against IdP JWKS endpoints
- Provider Abstraction: Defines interfaces for identity and access providers
- Configuration Management: Loads and validates configuration
- Error Handling: Comprehensive error types and handling
Core Classes
Section titled “Core Classes”CredentialBroker
Section titled “CredentialBroker”The main orchestrator class that coordinates the credential minting workflow:
class CredentialBroker { constructor( private config: BrokerConfig, private providers: { idp: Map<string, IdpProvider>, access: Map<string, AccessProvider> } ) {}
async mintCredentials(request: MintRequest): Promise<Credentials> { // 1. Validate OIDC token // 2. Check identity configuration // 3. Authenticate broker with its IdP // 4. Call access provider to mint credentials // 5. Return formatted credentials }}Provider Interfaces
Section titled “Provider Interfaces”interface IdpProvider { name: string; issuer: string; audience?: string | string[];
validateToken(token: string): Promise<TokenClaims>; getPublicKeys(): Promise<JWKSet>;}
interface AccessProvider { name: string; type: string;
authenticate(brokerToken: string): Promise<void>; mintCredentials( config: ProviderConfig, subject: string ): Promise<RawCredentials>;}Provider Implementations
Section titled “Provider Implementations”Identity Providers:
Auth0Provider: Auth0 OIDC integrationGitHubActionsProvider: GitHub Actions OIDC tokensKeycloakProvider: Generic Keycloak/OIDC supportOktaProvider: Okta identity platform
Access Providers:
AwsStsProvider: AWS STS AssumeRoleWithWebIdentityMinioProvider: MinIO STS integrationGcpProvider: Google Cloud IAMAzureProvider: Azure Active Directory
Configuration Schema
Section titled “Configuration Schema”interface BrokerConfig { brokerIdp: { name: string; issuer: string; audience: string; clientId: string; clientSecret: string; };
clientIdps: Array<{ name: string; issuer: string; audience?: string | string[]; jwksUri?: string; }>;
accessProviders: Array<{ name: string; type: string; endpoint?: string; region?: string; // Provider-specific config }>;
clientIdentities: Array<{ subject: string; idp: string; keys: Record<string, KeyConfig>; }>;}broker-server
Section titled “broker-server”The NestJS HTTP server that exposes the broker functionality as a REST API.
Architecture
Section titled “Architecture”Built on NestJS for:
- Dependency Injection: Clean, testable architecture
- Middleware Pipeline: Request validation, logging, security
- Modular Structure: Organized into feature modules
- Built-in Validation: Request/response validation
Key Modules
Section titled “Key Modules”CredentialsModule
Section titled “CredentialsModule”Handles credential minting endpoints:
@Controller('credentials')export class CredentialsController { constructor(private credentialsService: CredentialsService) {}
@Post('mint') async mintCredentials(@Body() request: MintRequestDto) { return this.credentialsService.mint(request); }
@Get('keys') async getAvailableKeys(@Query('token') token: string) { return this.credentialsService.getKeysForToken(token); }}ConfigModule
Section titled “ConfigModule”Manages configuration loading and validation:
@Module({ providers: [ { provide: CONFIG_TOKEN, useFactory: async () => { const config = await loadConfig(process.env.CONFIG_PATH); validateConfig(config); return config; }, }, ], exports: [CONFIG_TOKEN],})export class ConfigModule {}ProvidersModule
Section titled “ProvidersModule”Initializes and manages provider instances:
@Injectable()export class ProvidersService implements OnModuleInit { private idpProviders = new Map<string, IdpProvider>(); private accessProviders = new Map<string, AccessProvider>();
async onModuleInit() { await this.initializeProviders(); }
private async initializeProviders() { // Load and instantiate providers based on config }}Middleware & Guards
Section titled “Middleware & Guards”Security Middleware:
- CORS configuration
- Helmet for security headers
- Rate limiting
- Request ID generation
Validation Pipe:
app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true,}));API Endpoints
Section titled “API Endpoints”POST /credentials/mint
Section titled “POST /credentials/mint”Mint credentials for specified keys:
interface MintRequest { oidcToken: string; keys: string[];}
interface MintResponse { credentials: Record<string, any>; expiresAt: string;}GET /credentials/keys
Section titled “GET /credentials/keys”List available keys for a token:
interface KeysResponse { subject: string; keys: Array<{ name: string; provider: string; description?: string; }>;}CLI (voidkey)
Section titled “CLI (voidkey)”The Go command-line interface for interacting with the broker.
Architecture
Section titled “Architecture”Built with Cobra for:
- Command Structure: Clear command hierarchy
- Flag Management: Consistent flag handling
- Help Generation: Automatic help text
- Completion: Shell completion support
Command Structure
Section titled “Command Structure”voidkey├── mint # Mint credentials│ ├── --keys # Specific keys to mint│ ├── --all # Mint all available keys│ └── --output # Output format (env, json)├── list-keys # List available keys├── version # Show version info└── help # Show helpCore Commands
Section titled “Core Commands”mint Command
Section titled “mint Command”func mintCmd() *cobra.Command { cmd := &cobra.Command{ Use: "mint", Short: "Mint temporary credentials", RunE: func(cmd *cobra.Command, args []string) error { token := getOIDCToken() client := newBrokerClient()
response, err := client.MintCredentials(token, keys) if err != nil { return err }
formatter := getFormatter(outputFormat) return formatter.Format(response) }, }
cmd.Flags().StringSlice("keys", nil, "Keys to mint") cmd.Flags().Bool("all", false, "Mint all available keys") cmd.Flags().String("output", "env", "Output format")
return cmd}Output Formatters
Section titled “Output Formatters”Environment Variables:
export AWS_ACCESS_KEY_ID="ASIATESTACCESSKEY"export AWS_SECRET_ACCESS_KEY="testsecretkey"export AWS_SESSION_TOKEN="testsessiontoken"JSON Format:
{ "AWS_DEPLOYMENT": { "AccessKeyId": "ASIATESTACCESSKEY", "SecretAccessKey": "testsecretkey", "SessionToken": "testsessiontoken", "Expiration": "2024-01-15T12:00:00Z" }}Configuration
Section titled “Configuration”The CLI can be configured via:
- Environment Variables:
VOIDKEY_BROKER_URL=https://broker.example.comVOIDKEY_OIDC_TOKEN=eyJhbGciOiJSUzI1NiIs...VOIDKEY_OUTPUT_FORMAT=json- Configuration File:
broker_url: https://broker.example.comoutput_format: jsondefault_keys: - AWS_DEPLOYMENT - GCP_READONLY- Command Flags:
voidkey mint \ --broker-url https://broker.example.com \ --keys AWS_DEPLOYMENT \ --output jsonSandbox Environment
Section titled “Sandbox Environment”The Docker-based development environment for local testing.
Services
Section titled “Services”Keycloak
Section titled “Keycloak”Pre-configured identity provider with:
- Broker Realm: For broker authentication
- Client Realm: For client authentication
- Test Users: Pre-created test accounts
- Service Accounts: For broker-to-IdP auth
S3-compatible storage with:
- STS Support: Temporary credential generation
- Test Buckets: Pre-created storage buckets
- Admin Console: Web UI for management
- Configured Policies: Example IAM policies
Docker Compose Configuration
Section titled “Docker Compose Configuration”version: '3.8'
services: keycloak: image: quay.io/keycloak/keycloak:latest environment: KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin ports: - "8080:8080" volumes: - ./realms:/opt/keycloak/data/import command: start-dev --import-realm
minio: image: minio/minio:latest environment: MINIO_ROOT_USER: minioadmin MINIO_ROOT_PASSWORD: minioadmin MINIO_IDENTITY_OPENID_CONFIG_URL: http://keycloak:8080/realms/broker/.well-known/openid-configuration ports: - "9000:9000" - "9001:9001" command: server /data --console-address ":9001"Development Workflow
Section titled “Development Workflow”- Start Services:
docker-compose up -d- Configure Broker:
# Use sandbox-specific configurationbrokerIdp: issuer: "http://localhost:8080/realms/broker" clientId: "broker-service" clientSecret: "dev-secret"- Run Tests:
# Integration tests against sandboxnpm run test:integrationComponent Interactions
Section titled “Component Interactions”Request Flow
Section titled “Request Flow”sequenceDiagram
participant CLI as CLI Client
(Go)
participant API as Broker API
(NestJS Server)
participant Core as Broker Core
(TypeScript Library)
participant ClientIdP as Client IdP
(GitHub/Auth0/etc)
participant BrokerIdP as Broker IdP
(Keycloak/etc)
participant Provider as Access Provider
(AWS STS/GCP/etc)
CLI->>API: 1. POST /credentials/mint
with OIDC token
API->>Core: 2. Call CredentialBroker.mintCredentials()
Note over Core: Token Validation Phase
Core->>ClientIdP: 3. Fetch JWKS & validate token
ClientIdP-->>Core: 4. Token validation result
Note over Core: Broker Authentication Phase
Core->>BrokerIdP: 5. Authenticate broker (client credentials)
BrokerIdP-->>Core: 6. Broker access token
Note over Core: Credential Minting Phase
Core->>Provider: 7. Request temporary credentials
(using broker token)
Provider-->>Core: 8. Return scoped credentials
Core-->>API: 9. Return formatted credentials
API-->>CLI: 10. HTTP response with credentials
Error Handling
Section titled “Error Handling”Each component handles errors appropriately:
- broker-core: Typed errors with details
- broker-server: HTTP status codes and error responses
- CLI: User-friendly error messages
- Sandbox: Debug-friendly logging
Next Steps
Section titled “Next Steps”- Security Model - Security architecture details
- Configuration Guide - How to configure components
- Development Setup - Set up development environment
- API Reference - Detailed API documentation