The ERP Financial Agent uses a deployment-level isolation model: every customer (tenant) runs as a completely separate ECS service with its own FastAPI backend, its own MySQL database connection, and its own Cognito group. There is no shared query path between tenants. A single primary instance hosts the admin plane and the tenant registry; all other instances are isolated customer deployments.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.
Tenant Model
Each tenant maps to:| Resource | Naming Convention |
|---|---|
| ECS cluster | erp-agent-cluster-{tenant_id} |
| ECS service | erp-agent-service-{tenant_id} |
| RDS instance | erp-rds-{tenant_id} (or custom rds_identifier in registry) |
| Cognito group | {tenant_id} (slug, lowercase alphanumeric + hyphens) |
| DynamoDB registry entry | erp-agent-tenants table, PK tenant_id |
^[a-z0-9][a-z0-9-]{1,30}$ and cannot be production or staging (reserved).
Primary Instance vs Tenant Instances
Every deployment carries anexpected_tenant setting that controls which users may connect:
expected_tenant = "*"
Primary instance. Any authenticated user may connect. This is where the admin panel lives — tenant provisioning, user management, RBAC assignment, and the tenant registry are only available here.
expected_tenant = "acme"
Tenant instance. Only users whose
custom:tenantId Cognito attribute equals "acme" — or whose allowed_tenants includes "acme" — may connect. Requests from users on other tenants are rejected with HTTP 403.Principal.can_access_instance():
Cognito Custom Attributes
User-to-tenant mapping is stored in Amazon Cognito custom attributes set at account creation or updated by anorg_admin:
| Attribute | Type | Description |
|---|---|---|
custom:tenantId | String | The slug of the user’s home tenant. Becomes Principal.tenant. |
custom:tenants | String | Comma-separated list of additional tenant slugs the user may access. Parsed into Principal.allowed_tenants. |
custom:data_scope | String | cebes:1000,2000 or 1000,2000 — row-level centro_beneficio restriction. |
Cross-Instance Access
A user can be granted access to additional instances beyond their home tenant in two ways:Admin panel assignment
An
org_admin or platform_admin calls POST /api/admin/users/{username}/instances on the primary instance. This writes to custom:tenants in Cognito and, if needed, updates the DynamoDB RBAC overrides table.Tenant Provisioning
New tenant instances are never created from within the running backend. Provisioning requires CloudFormation + IAM privileges that the application must not hold. Instead, the primary instance dispatches a GitHub Actions workflow via the GitHub API:provision-new-tenant.sh) creates:
- A new ECS service in the existing cluster
- A new RDS MySQL database schema
- A new Cognito user group with the tenant slug
- An entry in the
erp-agent-tenantsDynamoDB registry
DynamoDB Tenants Registry
Theerp-agent-tenants table is the authoritative registry of all provisioned tenants. It stores:
| Field | Description |
|---|---|
tenant_id | PK — slug matching the provisioning pattern |
status | on / off / provisioning |
rds_identifier | RDS instance ID for start/stop operations |
updated_at | ISO timestamp of last status change |
list_tenants() function enriches registry data with live ECS and RDS state:
on (desired == running), starting (desired > running), off (desired == 0), unknown (ECS API failed).
Tenant Start / Stop
The admin panel can hibernate or wake tenant instances to control cost.start_tenant() and stop_tenant() operate both ECS and RDS in a single call:
- Start
- Stop
Admin Panel Isolation
The admin panel (tenant registry, user management, RBAC assignment) is served exclusively by the primary instance. Requests arriving at a tenant instance for admin endpoints are rejected because:require_primary_instance()checkssettings.expected_tenant != "*"and immediately returns HTTP 404 (not 403, to avoid revealing the panel exists). This fires before any permission check, so no user — regardless of role — can access admin endpoints on a tenant instance.- The tenant instance’s IAM role does not grant
dynamodb:Scanonerp-agent-tenantsorecs:UpdateServiceon other clusters.
The GitHub PAT used for workflow dispatch is stored in AWS Secrets Manager. The primary instance reads it at dispatch time via
secretsmanager:GetSecretValue. The secret name is configured in settings.github_token_secret_name. If the secret is absent or inaccessible, the API returns an actionable error rather than a generic 500.