feat: add AssumeRoleActivity for temporary LLM API token grants

Implements AWS AssumeRole-like pattern for Poimen:
- User/service requests temporary access with identity + scope
- AssumeRoleActivity exchanges credentials with OAuth2 auth server
- Returns JWT token valid for limited time (default: 1hr, max: 24hrs)
- Token used in all subsequent LLM API calls to api.riotpiao.com

Key features:
- Credentials from vault/K8s secrets (never hardcoded)
- Scope-based access control (llm:read, llm:read llm:write, llm:admin)
- Automatic token expiration tracking
- Retry support for transient auth failures (2x, 1.5s backoff)
- Configurable auth server endpoint

Usage pattern:
1. AssumeRoleActivity(identity, scope) → JWT token
2. LLMRouter uses token in LLMAuth config
3. All activity calls validated against token + scopes
4. Workflow optionally refreshes token before expiry

Security:
- No credentials in code/logs (env or vault only)
- Short-lived tokens (1hr default, 24hr max)
- Server-enforced scope validation
- Token revocation support

Activity registered: #10 (authentication category)
Knowledge base updated with full activity spec

New file: action/assume_role.go (5.2 KB)
This commit is contained in:
Test
2026-09-04 14:11:58 -07:00
parent ebf95506cd
commit 4a34c8e672
6 changed files with 366 additions and 4 deletions
+69 -2
View File
@@ -407,10 +407,76 @@
"dependencies": [],
"notes": "Network-dependent. First activity to run for context-aware routing. Fast timeout."
}
},
{
"name": "AssumeRoleActivity",
"description": "Request temporary JWT token for accessing LLM APIs (like AWS AssumeRole)",
"category": "authentication",
"inputs": {
"identity": {
"type": "string",
"description": "User/service identity requesting access",
"required": true,
"examples": ["[email protected]", "service:poimen-worker"]
},
"clientId": {
"type": "string",
"description": "OAuth2 client ID (from vault if not provided)",
"required": false
},
"clientSecret": {
"type": "string",
"description": "OAuth2 client secret (from vault if not provided)",
"required": false
},
"scope": {
"type": "string",
"description": "Scope of access (e.g., 'llm:read' or 'llm:read llm:write')",
"required": true,
"examples": ["llm:read", "llm:read llm:write", "llm:admin"]
},
"durationSeconds": {
"type": "integer",
"description": "Token validity duration in seconds (default: 3600, max: 86400)",
"required": false,
"default": 3600
},
"authServerUrl": {
"type": "string",
"description": "Auth server URL (from AUTH_SERVER_URL env if not provided)",
"required": false
}
},
"outputs": {
"token": {
"type": "string",
"description": "JWT token for calling api.riotpiao.com"
},
"expiresAt": {
"type": "integer",
"description": "Token expiration time (Unix timestamp)"
},
"expiresIn": {
"type": "integer",
"description": "Seconds until token expires"
},
"tokenType": {
"type": "string",
"description": "Token type (typically 'Bearer')"
}
},
"constraints": {
"defaultTimeout": "30s",
"isFlaky": false,
"recommendedRetries": 2,
"retryBackoff": 1.5,
"dependencies": [],
"notes": "Must run before LLM Router to provide auth token. Call early in workflow."
}
}
],
"metadata": {
"totalActivities": 9,
"totalActivities": 10,
"lastUpdated": "2025-08-31T00:00:00Z",
"categories": {
"repository": 1,
@@ -421,7 +487,8 @@
"notification": 1,
"approval": 1,
"storage": 1,
"memory": 1
"memory": 1,
"authentication": 1
}
}
}