- Post-provision S3 IAM helper (
src/iblai_infra/runtime_iam.py). Afterprovision/provision-envsucceeds, the CLI prints the exact S3-only minimum-privilege IAM policy JSON the operator needs to attach to a scoped runtime user in their own AWS account — and writes the same JSON to<workspace>/runtime-iam-policy.jsonso it can be piped intoaws iam put-user-policy --policy-document file://.... The policy scopes S3 to the literal bucket ARNs Terraform just created (no wildcards, nos3:*, no bucket-policy / lifecycle / encryption mutation). Skipped automatically forDeploymentType.CALL(no S3 buckets). - Three copy-paste
aws iamcommands in the post-provision output (create-user,put-user-policy,create-access-key) using<project>-<env>-s3-runtimeas the user name — operator pastes the resultingAccessKeyId+SecretAccessKeydirectly into.env.setup. - README sub-section under "Provision infrastructure" documenting the S3 IAM step + the scope table, plus a credential-set table clarifying that ECR pull credentials are a separate IBL-provided handoff, not part of this flow.
- Two-credential split end-to-end. Previously a single
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYfrom.env.setuphad to serve both ECR auth (IBL's account) and S3 access (customer's account) — works only when one key happens to have both scopes. Now:AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYcarry the S3 keys (customer-created post-provision) and are written to the root of/ibl/config.ymlby a new task in theibl_platformrole; consumed by DM / edX at runtime via iblai-cli-ops templating.- New
ECR_AWS_ACCESS_KEY_ID/ECR_AWS_SECRET_ACCESS_KEY(optionalECR_AWS_DEFAULT_REGION) carry the ECR keys (ibl.ai-provided). Theawsclirole writes these to~/.aws/credentials[default]profile on the host soaws ecr get-login-passwordfinds them without env-var overrides anywhere. - The four
Login to ECRtasks acrossibl_spa,ibl_launch_services,ibl_platform,ibl_service_updateno longer setAWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYenv-vars at command time — they rely on the default profile populated byawscli. SetupConfiggainsecr_aws_access_key_id,ecr_aws_secret_access_key,ecr_aws_default_region(all optional). Secret isField(exclude=True).runner.py::_build_extra_varspasses both sets as separate ansible extra-vars. WhenECR_AWS_*is empty, the S3 keys fall through to the ECR slot — backwards-compatible with single-key-set deployments.
.env.setup.examplenow shows two clearly-labeledAWS_*blocks (S3 + ECR) with usage / destination spelled out inline.- Section 4 of the README (non-interactive
.envflow) renumbered as a 3-step sequence (provision → mint S3 user → setup) so the IAM step isn't missed. - README credential-set table under "Provision infrastructure" gains a "Lives in" column documenting
/ibl/config.ymlroot vs~/.aws/credentials [default]so the operator knows exactly where each set lands on the server.
ibl_tenant_platformansible role — launches a tenantPlatform(Platform + admin User + UserPlatformLink) viarun_launch_stepswhenPLATFORM_NAMEis set to anything other thanmain. NOT a rawPlatform.objects.create()— the state machine fires every after_launch signal (default apps, edX hooks, UserPlatformLink flags). Wired into bothplaybook.yml(setup / setup-env) andlaunch_playbook.yml(launch / launch-env). Skips + logs on re-runs when the tenant already exists. Also writesPLATFORM_NAME=<KEY>(uppercase) at the root of/ibl/config.ymland enforcesPlatform.show_paywall=False+Platform.is_advertising=Falseas defense in depth. Surfaces the generated admin password via theIBLAI_FIXTURE_OUTPUTpipeline — printed once after the Rich Live display tears down, never persisted to disk.- Microsoft SSO writes
IBL_SPA.AUTH—microsoft_sso_confignow also patchesEXTERNAL_IDP_LOGOUT_URLandIBL_DIRECT_SSO_URL(usingmicrosoft_sso_tenant_id, falling back tocommon), then restarts the Auth + Mentor SPAs so the new auth flow takes effect. INSTANCE_RAM_GBhelper + 32 GB memory warning — non-blocking heads-up suggesting 64 GB (e.g.m5.4xlarge/r5.2xlarge) when the operator picks a 32 GB instance. Always shown in the interactive provision wizard andprovision-env; conditional inlaunch/launch-env(only when AI is enabled).- Final
ibl global-proxy reloadadded aspost_tasksin bothplaybook.ymlandlaunch_playbook.yml, so any nginx state touched by SSO roles (edX restarts ingoogle_sso_config/microsoft_sso_config) is reloaded before the playbook exits. RESERVED_ADMIN_USERNAMES+RESERVED_PLATFORM_NAMES—models.pyconstants, surfaced viais_reserved_admin_username()andis_reserved_platform_name()helpers and anInfraConfigmodel_validator.
- Stripe billing UI off by default —
IBL_SPA.MENTOR.STRIPE_ENABLED=falseandIBL_SPA.MENTOR.ENABLE_ADVERTISING=falseare now written unconditionally byibl_spa(fresh installs) andibl_launch_services(AMI launches). Behavior change: Stripe-using deployments must explicitly flipIBL_SPA.MENTOR.STRIPE_ENABLEDback to'true'post-setup. The previous "always on" SPA flag surfaced billing UI even when Stripe wasn't actually configured. - 100 GB minimum root volume for single / multi server — enforced by Pydantic (
InfraConfigmodel_validator gated onDeploymentType.SINGLE, plusMultiServerConfig.validate_volume_sizes) and matching interactive + CLI + .env input checks. Behavior change: values below 100 GB are now rejected upfront. DefaultComputeConfig.volume_sizebumped 50 → 100. Call-server unchanged (LiveKit only needs ~40 GB). ADMIN_USERNAME=ibl_adminrejected at every input layer — reserved for the SPA OAuth Application owner the platform itself maintains. New default suggestion isplatform_admin. Interactive prompts,.envparsers, and--admin-usernameflag all rejectibl_adminwith a clear reserved-name error. Behavior change: scripted deploys passingADMIN_USERNAME=ibl_adminmust rename.PLATFORM_NAME=mainrejected as an explicit input — unset / blank silently resolves tomain(preserving SSObackend_name=main-oauth2and skipping the tenant launcher). Behavior change: scripted deploys passingPLATFORM_NAME=mainshould drop the line.- README — refreshed against current playbook (16 roles, phase-grouped table), three deployment topologies, sizing guidance, tenant launcher, reserved-name rules. -50 lines net.
- All references to a specific canonical-client name from comments, docstrings, prompt instructions, error hints, and example .env files. Placeholders:
<client>for monorepo org names,acmefor tenant-key examples.
- Slow
_test_ssh()retry-path tests — five tests intests/ansible/test_runner.pyexercise the SSH-retry exhaust path (10 retries × 15 s sleep). They now mocktime.sleepalongside the existingsubprocess.runmock, cutting ~11 minutes off the full suite. Test count: 562 passing in ~1.3 s.
- Optional Microsoft (Azure AD) SSO setup via a new
microsoft_sso_configansible role. When the operator opts in (Y/N prompt duringiblai infra setup, or--microsoft-sso-client-idforiblai infra launch), the role does two things: (1) patchesIBL_EDX.IBL_EDX_BASE_OAUTH_SSO_BACKENDin/ibl/config.ymlvia direct Python yaml manipulation (since the block has nested dicts + a list, whichibl config save --setcannot round-trip), runsibl config save, and bounces edX so the new Django settings take effect; (2) creates anOAuth2ProviderConfigrow on the LMS for theazuread-oauth2slug, withbackend_namederived fromplatform_name,sync_learner_profile_data=True, and a Microsoft-specificother_settingsJSON carryingplatform_key,backend_uri, and the Azure AD federatedlogout_url. Idempotent — the heavyibl config save+ edX restart only run when the config block actually differs from the desired state, and theOAuth2ProviderConfigsave usescurrent(slug)to skip when the latest revision already matches SetupConfig.platform_name— top-level field (defaults tomain), prompted at the start of Step 2 (Platform Configuration). Lowercased + stripped on input. Drives both the SSObackend_name(<platform_name>-oauth2) and theother_settings.platform_key. Always populated; the SSO roles read it whether or not their feature flag is enabledSetupConfig.microsoft_sso_*fields —microsoft_sso_enabled,microsoft_sso_client_id,microsoft_sso_client_secret,microsoft_sso_tenant_id,microsoft_sso_organization. Client secret isField(exclude=True)so it never lands instate.json- Launch CLI flags —
--platform-name(defaultmain),--microsoft-sso-client-id(the trigger),--microsoft-sso-client-secret,--microsoft-sso-tenant-id,--microsoft-sso-organization. Same env-var pattern as Stripe / SMTP / Google SSO
- Optional Google SSO setup via a new
google_sso_configansible role. When the operator opts in (Y/N prompt duringiblai infra setup, or--google-sso-client-idforiblai infra launch), the role creates anOAuth2ProviderConfigrow on the LMS for the python-social-authgoogle-oauth2backend, bound tolearn.<base_domain>. Captures Client ID, Client Secret (no-echo password prompt), and an optional organization short_name. Secret isField(exclude=True)onSetupConfigso it never lands instate.jsonand rides extra-vars to ansible at run time only. Idempotent — re-runs check the latest revision and skip the save when values match SetupConfig.google_sso_*fields —google_sso_enabled,google_sso_client_id,google_sso_client_secret,google_sso_organization- Launch CLI flags —
--google-sso-client-id(the trigger),--google-sso-client-secret,--google-sso-organization. Same env-var pattern as Stripe/SMTP
- Pin direct runtime dependencies to currently running freeze versions for issue #1633 — updated
pyproject.tomlto exact pins foransible-core==2.19.9,boto3==1.42.97,pydantic==2.13.3,questionary==2.1.1,rich==15.0.0, andtyper==0.25.0, then regenerateduv.lockso lock and install metadata are aligned to the same tested dependency set.
- Fresh-provision LMS crash loop (
ibl_platformrole). Neweriblai-cli-ops(5.x+) ships an import-time check inibl-edx-sso-backend-app/constants.pythat rejects a missing or placeholderIBL_FERNET_KEY. Fresh bootstrap user_data writes a placeholder, so LMS/CMS crash-loop withImproperlyConfiguredand the "Wait for LMS to be ready" task times out at 40 retries. Ports the same fernet guard fromibl_service_updatetoibl_platform: reads the key, rotates only when empty/BAD_FERNET_KEY/the known template default, leaves real keys untouched. Idempotent
ibl-cliresolves to PyPI's wrong package on fresh provisions (ibl_cli_opsrole). Wheniblai-prod-imageswas installed viauv pip installof a git URL, uv silently ignored its[tool.uv.sources](project-only) and fell through to PyPI's unrelatedibl-cli==2.0.11, which is missingibl/templates/config/defaults.yml.ibl --helpthen crashed in the very next "Verify ibl CLI is available" task. The role now does a second explicituv pip install ... --reinstallofiblai-cli-opsat the operator-specified repo+tag (honoringcli_ops_subdirfor monorepo layouts), overriding the wrong transitive dependency. Applies to bothsingle-serverandcall-servertemplates
- Private-access gate fires on
provision→ "Run platform setup now?" path. The post-provision shortcut (app._offer_setup) bypassed_confirm_private_access_or_abort()because it never reached_run_setup_provisioned/_run_setup_interactive/_run_resetup. Operators going fromiblai infra provisionstraight into setup now see the same prerequisites notice + Y/N confirm before any prompts collect input
- Monorepo subdirectory installs —
--cli-ops-repo/--prod-images-repo(and the matching setup prompts) now accept arepo/subdirpath, e.g.<client>-iblai-infra-ops/<client>-iblai-prod-images. The ansible role appends&subdirectory=<subdir>to the install URL so a single client monorepo can host bothiblai-cli-opsand the prod-images package parse_repo_path()helper inmodels.py— splits operator input into(repo, subdir). Bareiblai-cli-opskeeps the canonical behavior; subdir-form unlocks per-client monorepo deploymentscli_ops_subdir/prod_images_subdirextra-vars passed throughAnsibleRunnerto theibl_cli_opsrole (single-server + call-server templates)
- Multi-server deployment type —
iblai infra provisionnow offers a deployment type selector: single-server (existing) or multi-server. Multi-server provisions N app servers (2-10) in public subnets behind an ALB + 1 services server in a private subnet, with optional managed RDS MySQL/PostgreSQL and Redis ElastiCache DeploymentTypeenum —SINGLE/MULTIonInfraConfig, defaults toSINGLEfor backward compatibilityMultiServerConfigmodel — app server count/type/volume, services server type/volume, managed service toggles. DB passwords and Redis auth tokens generated at runtime, excluded from state serialization viaField(exclude=True)- Multi-server Terraform templates (
templates/aws/multi-server/) — VPC with 4 subnet tiers (public/private/database/cache), NAT gateways per AZ, 6 security groups (ALB, app, services, RDS, Redis, EFS), EFS shared media storage, optional RDS MySQL 8.4 + PostgreSQL 15 (multi-AZ), optional Redis ElastiCache (multi-AZ, encrypted) - Multi-server wizard prompts — interactive configuration for app server count, instance types, volume sizes, managed database and Redis toggles
- Multi-server review panel — shows server counts, managed services status, subnet tiers
launchmulti-server flags —--deployment-type,--app-server-count,--services-instance-type,--services-volume-size,--enable-mysql,--enable-postgres,--enable-redis- Type column in
listcommand — showssingleormulti (N)for each environment - New resource labels — NAT Gateway, Elastic IP, RDS Database, DB Subnet Group, Redis Cluster, Cache Subnet Group, EFS File System, EFS Mount Target
- Terraform gitignore entries —
.terraform/,*.tfvars,*.tfstateadded to.gitignore
- Smoke tests in service-update — after nginx restart, verifies SSO login for all 4 browser test users, DM API accessibility, and Mentor chat endpoint. Reports a clear pass/fail summary in CI logs before handing off to Playwright tests. Advisory only (does not fail the pipeline)
- Target group registration order —
register_target()now registers the new instance FIRST, then deregisters old targets. Prevents empty target group (ALB 503) if the pipeline fails between deregister and register
resetupcommand —iblai infra resetup <name>re-configures an existing environment with a new base domain and fresh secrets. Rotates all secrets (ibl config rotate-secrets -f --include-auth), syncs PostgreSQL and MySQL passwords, then restarts all serviceslaunchcommand —iblai infra launchprovisions AWS infrastructure from a pre-built AMI via Terraform (VPC, ALB, ACM certs, Route53, EC2) and configures the platform via Ansible in a single non-interactive command. All input via CLI flags for CI/CD workflowslaunch-envcommand —iblai infra launch-envreads a.envfile from the current directory, shows a summary with masked secrets, confirms, then launches. Simplest path for local useservice-updatecommand —iblai infra service-updateupdates container images and restarts services without infrastructure changes or secret rotation. Two modes:--hostfor existing servers,--ami-idto launch EC2 from AMI + update + register in ALB target group. Designed for CI/CD image update workflows.env.example— template with all launch variables using safe placeholder values (RFC 5737 IPs, AWS example keys)- AMI support in Terraform — new
ami_idandskip_user_datavariables allow launching EC2 from a custom AMI instead of vanilla Ubuntu - Launch Ansible playbook —
launch_playbook.ymlwith lean roles for AMI-based deployments - Service update Ansible playbook —
service_update_playbook.ymlwith 2 roles (ibl_cli_ops, ibl_service_update) for day-2 image updates ibl_launchrole — starts databases, sets domain, rotates secrets, syncs PostgreSQL and MySQL passwords after rotationibl_launch_servicesrole — ECR login, DM update, edX stop/start, SPA restart with health checks, proxy reloadibl_service_updaterole — ECR login, edX stop/prune/config save/start, DM config save/update, DM migrations, SPA restart with health checks, nginx restart- SPA health checks — all SPA launches/restarts now verify HTTP 200 on Auth (5000), Mentor (5001), Skills (5002) with 10 retries at 15s intervals
- Ansible progress display — shows current task description (e.g. "Wait for DM web to be ready") instead of just "Running"
- Split
final_stepsrole into 3 focused roles:integrations(OAuth/OIDC, edX-manager, DM auth-setup, edX sync),admin_setup(OpenAI key, super admins, CSRF domains, LLM key),data_seeding(flows, LLM registry, mentors, RBAC, TimescaleDB views, analytics views) - TimescaleDB support —
ENABLE_TIMESCALEDB=trueset in platform config,setup_timescale_views --full-setupandrefresh_analytics_viewsrun during data seeding HIDE_ANALYTICS='false'— set as quoted string in SPA mentor config- CLI ops release tag prompt — both setup and resetup now prompt for iblai-cli-ops release tag
- iblai-prod-images installation — ibl_cli_ops role installs via
uv pip install iblai-images[sumac]fromiblai/iblai-prod-images, which pins both CLI ops and all container image versions - AnsibleRunner parameterization — supports multiple playbooks and role label sets (setup, launch, service-update)
- EC2 launch + target group helpers —
launch_instance,wait_for_instance_running,register_target,terminate_instanceinproviders/aws.py
- Image versions controlled by iblai-prod-images — removed all hardcoded image tags from Ansible roles (DM, edX, MFE, postgres, SPA, supporting services). The CLI now rejects overrides; versions are pinned by the
iblai-imagespackage - Removed image tag prompts — setup no longer asks for DM, edX, or SPA image tags.
SetupConfigmodel no longer has image tag fields - Removed hardcoded MySQL 8.0.40 — was causing version mismatch crashes when AMI data was created with MySQL 8.4.0. The CLI's
default.ymlnow provides the correct version
- PostgreSQL password sync after secret rotation — resetup and launch capture the current password before rotation and use it to ALTER USER after rotation
- MySQL password sync after secret rotation — same capture-before-rotate pattern for both root and openedx MySQL users
- PostgreSQL data directory ownership — resetup restores postgres data dir to uid 999 before restarting, preventing "Permission denied" errors after the recursive chown on /ibl
- State base_domain update on resetup —
iblai infra listnow shows the new domain after resetup destroycommand handlesprovider="launch"— launch-created projects can be properly destroyed
- Super admin credentials prompt — setup wizard asks for admin username (default
ibl_admin), email, and password; creates superuser in both DM and LMS via Django shell infinal_stepsrole - Optional OpenAI API key prompt — when provided, creates a
GlobalCredentialentry in DM withis_preferred=True; skippable with blank input UseMainLLMKeyconfiguration —final_stepsrole enablesuse_main_key=Truefor themainplatform so tenants inherit the global LLM credentialopenai_api_key,admin_username,admin_email,admin_passwordfields onSetupConfigmodelibl_webOAuth2 application created in LMS (public, password grant) — client ID used forIBL_SPA.AUTH.IBL_OAUTH2_CLIENT_ID- CSRF exempt domain seeding — 24 platform subdomains added to
CsrfExemptDomainin LMS for CORS support - Unified API gateway enabled by default (
IBL_REVERSE_PROXY.ENABLE_UNIFIED_API_GATEWAY=true) - MFE image (
ibl-edx-mfe-pro:sumac.0.3.2) and JWT auth (ENABLE_JWT_AUTH=True) set inibl_platformrole - CORS enabled for edX (
IBL_EDX_CORS_HEADER.CORS_ORIGIN_ALLOW_ALL=true) - DM RBAC enabled (
IBL_DM.ENABLE_RBAC=true,IBL_DM.ENABLE_RBAC_SEEDING=true) IBL_DM.ALLOW_TENANTS_TO_USE_MAIN_LLM_CREDENTIALS=trueset before DM launchibl-edx-uwsgiplugin ensured inIBL_EDX.PLUGINSvia Python yaml (safe append)- Full SPA configuration:
DEFAULT_APP_URL,ENVIRONMENT,SKIP_TEST,ENABLE_APP_SITE_ASSOCIATION,CANVAS_ADMIN_ONLY,STRIPE_ENABLEDwith quoted boolean values written via Python yaml ibl edx sync-with-manager --usersinfinal_stepsrole- Seed commands in order:
seed_flows→seed_llm_registry→seed_base_mentors→seed_rbac_data ibl config save && ibl global-proxy reloadafter SPA launches
- DM container verification now waits for the web endpoint to respond (up to 10 minutes) instead of only checking
docker ps— catches crash-looping containers that still show as "Running" - DM verification checks
RestartCountand fails with actionable error (suggestsibl dm migrate) if container has restarted more than 3 times - edX container verification also checks LMS
/heartbeatendpoint readiness and restart count GlobalCredential.valuestored as dict directly (notjson.dumps) —JSONFieldauto-serializes; double-serializing caused 500 on admin page- SPA quoted boolean values (
'true'/'false') written via Python yaml to avoidibl config save --setquoting syntax errors ibl-edx-uwsgiplugin appended via Python yaml to avoidibl config printvaluelist parsing errors
- pgvector extension task used hardcoded
postgresuser andibl_dm_dbdatabase — now reads$POSTGRES_USERand$POSTGRES_DBfrom container environment, matching actual DM postgres configuration (ibl/dlmanager) pg_isreadyhealth check also updated to use$POSTGRES_USERinstead of hardcodedpostgres- Ansible runner reported false failures when tasks with
ignore_errors: trueemittedfatal:lines — runner now trustsproc.returncodeas the primary success signal and shows ignored errors as warnings instead of failing the run - Removed
ignore_errors: truefrom pgvector task since it should now succeed with correct credentials
iblai infra bootstrapcommand — set up the IBL platform on any existing server (any cloud, bare metal) without Terraform provisioning- Interactive bootstrap wizard collects server IP, SSH key, domain, image tags, and AWS/GitHub credentials
- Bootstrap projects tracked with
provider="bootstrap"—list,status, anddestroyall work - Destroy guard for bootstrap projects skips Terraform teardown and marks project as destroyed
- "Bootstrap existing server" option in landing screen menu
edx_supporting_service_defaults— set default image tags for edX supporting services (MySQL 8.0.40, Elasticsearch, Redis, MongoDB) during provisioning- Architecture diagrams (single-server and multi-server AWS topologies) in README
- Branded README header with badges, install instructions, and dependency documentation
- MySQL version pinned to 8.0.40 instead of 8.4.0 — 8.4.0 caused compatibility issues with edX
- LMS container health verified (running and not restarting) before OAuth2 application creation
- Retries added to OAuth2 creation for container restart resilience
- Postgres data directory recursively chowned to UID 999 before DM launch
/ibl/directory ownership set to SSH user before any services launchapache2-utilsadded to prerequisites forhtpasswdavailability- LMS health check and OAuth creation use
docker execinstead oftutorCLI - Langfuse secrets generated before DM launch when AI features are enabled
ibl_spaAnsible role — creates OAuth2 Application in edX for SPA SSO, sets SPA config defaults, authenticates Docker with ECR, and launches Auth, Mentor, and Skills SPA containers- SPA image tag prompts in setup wizard: Auth SPA (
1.13.15), Mentor SPA (0.35.14), Skills SPA (0.9.8) spa_auth_image_tag,spa_mentor_image_tag,spa_skills_image_tagfields onSetupConfigmodel- 3 new platform subdomains:
api.,platform.,prometheus. web.data.subdomain for SPA data API
- Playbook now runs 9 roles: docker, awscli, python, ibl_cli_ops, ibl_platform, ibl_dm, ibl_edx, ibl_spa, final_steps
_build_extra_vars()passes SPA image tags to playbook- ACM certificate domain lists updated: cert 1 adds
api.andweb.data.; cert 2 addsplatform.andprometheus. IBL_SUBDOMAINSupdated from 16 to 19 entries (addedapi,web.data,platform,prometheus; removedstatus)
- AI features prompt — asks user whether to enable AI for DM (
IBL_DM.ENABLE_IBL_AIandIBL_DM.ENABLE_IBL_AI_PLUS), defaults to enabled enable_aifield onSetupConfigmodel, passed through to Ansible extra varsibl_platformrole configures both AI settings based on user choice
- Create
ibl_local_defaultdocker network inibl_platformrole after global proxy launch — DM compose requires it as an external network but the proxy only createsibl_default - Add container verification to
ibl_dmrole — fails with actionable error if no DM containers are running after launch - Add container verification to
ibl_edxrole — fails if no edX containers are running after launch - Broadened DM container filter from
ibl-dm-protoibl_dmto match actual container naming
- Default DM image tag changed from
4.190.0-aito4.189.1-ai— previous tag did not exist in ECR, causing silentibl dm launchfailure
- Full platform setup via Ansible — 8 roles: docker, awscli, python, ibl_cli_ops, ibl_platform, ibl_dm, ibl_edx, final_steps
- DM and edX image tag prompts with defaults (
4.189.1-ai,sumac.2.4.13); sets ECR image URIs before launch ibl_platformrole configures edX version, base domain, environment, and DM/edX container imagesibl_dmrole runsibl dm launch(timeout 1800s)ibl_edxrole runsibl edx launch(timeout 3600s)final_stepsrole runsibl config save,ibl global-proxy reload,ibl launch --ibl-oauth --ibl-oidc --ibl-edx-manager, andibl dm auth-setupdm_image_tagandedx_image_tagfields onSetupConfigmodel
- Simplified runner to single-phase Ansible execution (removed two-phase SSH/Fabric approach)
_build_extra_vars()now passesbase_domain,edx_version,env_config,dm_image_tag,edx_image_tagto playbook- Removed
fabricdependency — all remote execution handled by Ansible
- Tests updated to match runner rewrite — removed tests for deleted JSON-parsing methods, added tests for all 8 roles
- Comprehensive pytest test suite — 380 tests covering models, providers, Terraform runner, Ansible runner, CLI commands, prompts, validators, review flows, state management, and UI helpers
- Dev dependencies in
pyproject.toml:pytest>=8.0,pytest-cov>=4.1 - Pytest configuration:
--strict-markers,testpaths = ["tests"],slowmarker - Test coverage for all enum combinations (SSH method × cert method × environment), IP/CIDR/domain validators, and session persistence paths
load_state()now skips corruptstate.jsonfiles instead of crashing — previously a single corrupt workspace would prevent loading any project by name- Replaced fragile
AnsibleRunner.__new__()hack in_run_setup()with a directshutil.which()check for ansible-playbook
iblai infra authcommand — switch or re-authenticate AWS credentials at any time- Session persistence — credentials saved to
~/.iblai-infra/session.jsonafter authentication; reused across all commands until explicitly cleared or expired - Interactive landing screen — running
iblai infrashows a branded menu with arrow-key navigation to launch any command directly - Type-to-filter for long lists — regions, AWS profiles, instance types, and key pairs use
questionary.autocomplete()for instant filtering
- Credential resolution order: explicit
--profileflag → saved session → interactive wizard (no silent auto-detection) prompt_credentials()acceptsshow_stepparameter — step header only shown during the full 5-step wizardrun_provision_wizard()acceptsshow_bannerparameter — avoids double banner when launched from the landing screen menu- Simplified saved session display: shows "Authenticated — user (account)" instead of full ARN details
- Command names in instructional text now highlighted with
[brand]color - Dynamic versioning —
pyproject.tomluses[tool.hatch.version]pointing to__init__.py
ctx.invoke()passingOptionInfoobjects instead of actual values to Pydantic models — now passes explicit defaults- Volume type default mismatch (
"gp3 (recommended)"vs"gp3") causing validation error - Non-ASCII em dashes in Terraform security group descriptions rejected by AWS API
- Duplicate "Authenticated as" messages during permission checks
- Double banner when launching provision from the landing screen menu
- Removed "recommended" labels from instance type and volume type choices
- Interactive authentication fallback — when AWS credentials are missing or invalid, any command that needs auth now offers to launch the credentials wizard instead of failing
- Shared
_resolve_credentials()helper in CLI that tries env vars,~/.aws/profiles, then falls back to the interactive Step 1 wizard
iblai infra permissionscommand — displays minimum IAM policy JSON required for provisioning--checkflag for dry-run permission verification against active AWS credentials (EC2, ELB, S3, ACM, Route 53, IAM, STS)--profileand--regionflags for targeting specific credentials during permission checks- Branded landing screen when running
iblai infrawith no arguments — shows all available commands and a getting-started guide
- Interactive provisioning wizard with 5-step flow (credentials, compute, network, DNS, review)
- AWS authentication: profile, access keys, or environment variables with STS validation
- EC2 single-server provisioning with configurable instance type and volume
- VPC, public subnets (multi-AZ), internet gateway, and route tables
- Application Load Balancer with security groups
- Three certificate modes: ACM (auto-managed via Route53), upload (IAM server cert), or none (HTTP only)
- Three SSH key modes: generate Ed25519 keypair, provide existing public key, or use AWS key pair
- SSH access restricted to user-provided VPN IP
- S3 buckets for backups, media, and static files
- 19 ibl.ai platform subdomain records (when using Route53)
- Real-time Terraform progress with JSON event streaming and Rich Live display
iblai infra provision— interactive provisioning wizardiblai infra destroy— destroy infrastructure with double-confirmation for productioniblai infra status <name>— show infrastructure details and workspace infoiblai infra list— list all managed environments- ibl.ai branded terminal UI with Rich theme and questionary styling
- Project state persistence at
~/.iblai-infra/projects/ - Workspace visibility showing Terraform files during and after provisioning