Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/yohangr3/agentelanggrafh/llms.txt

Use this file to discover all available pages before exploring further.

The ERP Financial Agent is deployed entirely on AWS. This page walks through creating and configuring every required service before you run the CloudFormation stack. All steps assume the us-east-1 region unless otherwise noted.
All service-to-service communication uses IAM roles — no access keys are stored in the application code. The ECS task role grants exactly the permissions listed in this guide; no more, no less.

Required AWS services at a glance

ServicePurpose
Amazon BedrockHosts Claude Sonnet 4.5 (LLM) and Amazon Titan Embed v2 (embeddings)
AWS CognitoIssues and validates JWT tokens for every API request
Amazon ElastiCache (Redis 7.0)Session checkpoints, rate-limit counters, uploaded-file cache
Amazon RDS (MySQL 8)SAP/ERP financial data (read-only access by the agent)
Amazon DynamoDBLong-term conversation memory and query audit log
Amazon ECRContainer image registry for backend and frontend
Amazon ECS FargateServerless container runtime (2 vCPU / 4 GB per task)
AWS Secrets ManagerSecure storage for the RDS password (production)

Step-by-step setup

1
Enable Bedrock model access
2
Amazon Bedrock model access is disabled by default and must be requested per region.
3
  • Open the Amazon Bedrock console in us-east-1.
  • In the left navigation choose Model accessManage model access.
  • Enable the following models:
    ModelInference profile IDUse
    Claude Sonnet 4.5us.anthropic.claude-sonnet-4-5-20250929-v1:0General LLM (SQL gen, intent, explanation)
    Claude Sonnet 5us.anthropic.claude-sonnet-5Reasoning-intensive nodes
    Amazon Titan Embed Text v2amazon.titan-embed-text-v2:0Schema registry embeddings
  • Submit the access request. Model access is typically granted in under a minute for on-demand models.
  • 4
    The agent uses cross-region inference profiles (us.* prefix), not base model IDs. The profile ID must be used exactly as shown in BEDROCK_MODEL_ID; substituting the bare model ID will fail at runtime.
    5
    Create the Cognito User Pool
    6
    The application validates every API request against a Cognito User Pool. The pool ID is required at startup via COGNITO_USER_POOL_ID.
    7
  • Open the Cognito console and choose Create user pool.
  • Configure sign-in options:
    • Sign-in identifier: Email (required)
    • Optional: Username (if you want cognito:username in tokens)
  • Under Required attributes add:
    • email
    • Custom attribute custom:tenantId (string, mutable) — used for multi-tenant isolation
    • Custom attribute custom:company (string, mutable) — displayed in the UI
  • Under App clients create a client with:
    • Auth flows: ALLOW_USER_SRP_AUTH, ALLOW_REFRESH_TOKEN_AUTH
    • Token validity: Access token 1 hour, ID token 1 hour, Refresh token 30 days
    • Note the Client ID (set as COGNITO_CLIENT_ID)
  • Note the User Pool ID from the pool overview page (set as COGNITO_USER_POOL_ID).
  • 8
    Required Cognito groups
    9
    The RBAC system maps Cognito group membership to roles and permissions. Create these groups in User Pool → Groups:
    10
    Group nameRoleTypical permissionsplatform_adminPlatform administratorFull access including RBAC overrides and cross-tenant operationsorg_adminOrganisation administratorUser management, all financial data modulesfinance_leadFinance leadAll financial modules, export, file uploadanalystAnalystRead + export on assigned modulesviewerRead-only viewerQuery only, no export
    11
    The legacy group names admin, admins, and administrators are recognised as aliases for org_admin for backwards compatibility. New deployments should use platform_admin and org_admin directly.
    12
    Assign users to groups in the Cognito console or via aws cognito-idp admin-add-user-to-group. The group list appears in the cognito:groups claim of the ID token and is read by resolve_principal() in src/core/auth.py.
    13
    How JWT validation works
    14
    Each API request must include Authorization: Bearer <id_token>. The CognitoJWTValidator class in src/core/auth.py:
    15
  • Extracts the kid (key ID) from the JWT header.
  • Fetches the JWKS document from https://cognito-idp.{region}.amazonaws.com/{user_pool_id}/.well-known/jwks.json (cached for 1 hour, auto-refreshed on key rotation).
  • Verifies the RS256 signature using the matching public key.
  • Checks token_use == "id" and validates the issuer URL.
  • When COGNITO_CLIENT_ID is set, also verifies the aud claim.
  • 16
    Set up RDS MySQL
    17
  • Open the RDS console and create a MySQL 8.0 instance.
  • Recommended instance class for production: db.t3.medium or larger.
  • Enable Multi-AZ for production workloads.
  • Note the Endpoint (set as DB_HOST).
  • Create a database named iafinagent (or your preferred name; set as DB_NAME).
  • Restrict the security group to allow inbound MySQL (3306) only from the ECS security group.
  • 18
    The agent opens a read-only connection to RDS. Create a dedicated database user with SELECT grants only — do not use the master user. See the Database configuration guide for connection pool details.
    19
    Create ElastiCache Redis
    20
    The CloudFormation template in infra/cloudformation/services.yaml provisions a cache.t3.micro Redis 7.0 cluster automatically. If you are provisioning manually:
    21
  • Open the ElastiCache console and create a Redis OSS cluster.
  • Engine version: 7.0, Node type: cache.t3.micro (production: cache.r6g.large).
  • Subnet group: use the same private subnets as ECS.
  • Note the Primary endpoint and set REDIS_URL=redis://<endpoint>:6379/0.
  • 22
    Create DynamoDB tables
    23
    Two tables are required. The CloudFormation stack creates them automatically; for manual setup:
    24
    # Conversations table (long-term memory)
    aws dynamodb create-table \
      --table-name erp-agent-conversations \
      --billing-mode PAY_PER_REQUEST \
      --attribute-definitions \
        AttributeName=user_id,AttributeType=S \
        AttributeName=session_id,AttributeType=S \
      --key-schema \
        AttributeName=user_id,KeyType=HASH \
        AttributeName=session_id,KeyType=RANGE
    
    # Enable TTL
    aws dynamodb update-time-to-live \
      --table-name erp-agent-conversations \
      --time-to-live-specification "Enabled=true,AttributeName=ttl"
    
    # Query audit log table
    aws dynamodb create-table \
      --table-name erp-agent-query-log \
      --billing-mode PAY_PER_REQUEST \
      --attribute-definitions \
        AttributeName=query_id,AttributeType=S \
        AttributeName=timestamp,AttributeType=S \
      --key-schema \
        AttributeName=query_id,KeyType=HASH \
        AttributeName=timestamp,KeyType=RANGE
    
    25
    Create ECR repositories
    26
    aws ecr create-repository --repository-name erp-agent/backend \
      --image-scanning-configuration scanOnPush=true
    
    aws ecr create-repository --repository-name erp-agent/frontend \
      --image-scanning-configuration scanOnPush=true
    
    27
    Store the DB password in Secrets Manager (production)
    28
    In production the database password is never stored as a plain-text environment variable. Instead:
    29
    aws secretsmanager create-secret \
      --name erp-agent/db-password \
      --description "Database password for ERP Financial Agent" \
      --secret-string "your-secure-password"
    
    30
    Note the returned ARN and set DB_PASSWORD_SECRET_ARN in the CloudFormation parameter ExistingDBPasswordSecretArn. The application’s resolve_secrets() function fetches the value at startup and injects it into the db_password setting field.

    IAM permissions for the ECS task role

    The CloudFormation template creates two IAM roles automatically. The table below lists every permission granted to the ECS task role (erp-agent-task-role-<env>), which is the role assumed by the running container.
    {
      "Effect": "Allow",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream",
        "bedrock:Converse",
        "bedrock:ConverseStream"
      ],
      "Resource": [
        "arn:aws:bedrock:*::foundation-model/anthropic.*",
        "arn:aws:bedrock:*::foundation-model/amazon.titan-embed-*",
        "arn:aws:bedrock:*:<account_id>:inference-profile/*"
      ]
    }
    
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:PutItem",
        "dynamodb:GetItem",
        "dynamodb:Query",
        "dynamodb:UpdateItem",
        "dynamodb:DescribeTable",
        "dynamodb:CreateTable",
        "dynamodb:UpdateTimeToLive"
      ],
      "Resource": [
        "arn:aws:dynamodb:<region>:<account_id>:table/erp-agent-conversations",
        "arn:aws:dynamodb:<region>:<account_id>:table/erp-agent-query-log",
        "arn:aws:dynamodb:<region>:<account_id>:table/erp-agent-rbac-overrides"
      ]
    }
    
    {
      "Effect": "Allow",
      "Action": ["secretsmanager:GetSecretValue"],
      "Resource": ["arn:aws:secretsmanager:<region>:<account_id>:secret:erp-agent/db-password*"]
    }
    
    The ECS execution role (erp-agent-execution-role-<env>) also holds secretsmanager:GetSecretValue on the same ARN so the ECS agent can inject the secret at task launch.
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject"],
      "Resource": ["arn:aws:s3:::erp-agent-exports-<account_id>/*"]
    }
    
    {
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken",
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage"
      ],
      "Resource": "*"
    }
    

    Deploying with CloudFormation

    Once all services exist, deploy the full stack with:
    aws cloudformation deploy \
      --stack-name erp-agent-services-production \
      --template-file infra/cloudformation/services.yaml \
      --capabilities CAPABILITY_NAMED_IAM \
      --parameter-overrides \
        EnvironmentName=production \
        DatabaseHost=iafinagent.choi0eaq2mr7.us-east-1.rds.amazonaws.com \
        DatabaseName=iafinagent \
        DatabaseUser=admin \
        DatabasePassword=CHANGE_ME \
        CognitoUserPoolId=us-east-1_2qpNBGCmb \
        CognitoClientId=70qcoifagu4tk20qnn2q8d9rjs \
        RbacEnforce=false \
        ExpectedTenant='*'
    
    DatabasePassword is stored immediately in Secrets Manager by the CloudFormation stack and is never written to disk or logs after that. The NoEcho: true property on the parameter prevents it from appearing in CloudFormation events.

    Build docs developers (and LLMs) love