The current setup loop for a single foundation concern:
terraform output -raw <output-name> for the values the outside admin needs.terraform apply.This works. It also has six problems:
The Setup cartridge addresses all six by giving operators a single, navigable list of foundation setups, each with its current state, the values to hand off, the inputs to capture, and the verification step. Same pattern GitBook uses for setup-heavy products.
v1 ships the flows we already have doctrine for. Each flow has a wizard with stages.
| Flow | Outside party | What the operator hands off | What they get back |
|---|---|---|---|
| Azure SSO | PayCargo Azure admin | ACS URL, Entity ID, prereq checklist (Step 6.p section 12) | SAML metadata URL, confirmation of conditional access MFA |
| DNS records | PayCargo Systems / DNS admin | 3 batches of DNS records (host CNAMEs, ACM validation, SES records) | Confirmation that records are added |
| Salesforce Sandbox | PayCargo Salesforce admin | Callback URLs, the Howto.docx walking through Connected App setup | Consumer Key, Consumer Secret (stored in Secrets Manager via the cartridge) |
| Salesforce Production | Same admin, in production org | Same shape as Sandbox, different callback paths | Same shape |
| Custom domain certs | PayCargo DNS admin | ACM validation CNAMEs | Confirmation, then automated cert issuance |
| SES domain | PayCargo DNS admin | SES verification + DKIM records | Verification confirmation |
Future flows (when their doctrine lands):
Each flow goes through stages. The cartridge tracks state in DDB so the operator opens the cartridge and immediately sees where every flow is.
Each transition is logged. The operator gets a stage badge per flow: green when verified, amber for in-flight, gray for not-started. The cartridge home page is a list of flows with their stage badges.
The single most important UX element. Modeled on how operators actually use the doctrine today (manually copying outputs into Slack).
Each handoff has:
handoff-sent.┌────────────────────────────────────────────────────────────────┐ │ Send to Azure admin │ │ │ │ Subject: PayCargo Forge SAML configuration │ │ │ │ Hi, │ │ │ │ Please configure the PayCargo Forge enterprise application in │ │ Azure with the following SAML service provider details: │ │ │ │ ACS URL: https://ai-sandbox-obs-app-95922820.auth │ │ .us-east-1.amazoncognito.com/saml2/idpresponse │ │ Entity ID: urn:amazon:cognito:sp:us-east-1_fJLnhjqnl │ │ │ │ The full prereq checklist (scopes, conditional access policy, │ │ etc.) is attached. │ │ │ │ Please return: SAML metadata URL │ │ │ │ Thanks, │ │ Mike Tang │ │ │ │ [Copy as Slack] [Copy as email] [Download Howto.docx] │ │ [Mark as sent] │ └────────────────────────────────────────────────────────────────┘
Each return value is a labeled input field with:
┌────────────────────────────────────────────────────────────────┐ │ Capture Azure admin's response │ │ │ │ SAML metadata URL: │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ https://login.microsoftonline.com/.../federationmetadata/. │ │ │ └────────────────────────────────────────────────────────────┘ │ │ [Test fetch] (fetches the URL, validates XML, parses Entity)│ │ │ │ Conditional access MFA on the app: ○ Confirmed ● Not yet │ │ │ │ [Save inputs] (writes to SSM, generates Terraform snippet) │ └────────────────────────────────────────────────────────────────┘
System admins only. Cognito group admins. Same gate as the obs Admin tab.
Federated SSO users without admin group cannot see this cartridge in the Console launcher (it doesn't appear on their tile grid). Direct URL access returns 403 from the cartridge backend.
The Setup cartridge is the system admin's operating surface. Doctrine 8.8 says system admins are Cognito-native; the Setup cartridge runs under that authority. SSO users do not configure the platform; they request access to it via the Request Roles cartridge.
The instructions inside each wizard (the "what the outside admin needs to do" text) are content. They belong in the CMS (Step 6.s) when CMS lands.
Until then, instructions ship as files inside the Setup cartridge code (similar to how the chatbot ships its system prompt). Each flow has a markdown file with the prereq narrative; the cartridge renders it as the wizard's middle pane.
When CMS lands (per Step 6.s):
Three patterns for where return values land. Each flow picks one.
| Pattern | When to use it | Write target | Apply flow |
|---|---|---|---|
| SSM parameter | Non-secret config Terraform reads at plan time (e.g., SSO metadata URL, OAuth callback URLs) | /forge/setup/<flow>/<input> |
Terraform reads SSM as a data source; next apply picks up the change. Operator triggers apply via the existing Terraform Apply workflow. |
| Secrets Manager | Secret values (OAuth client_secret, API keys) | /forge/oauth/clients/<provider> etc. |
Terraform creates the secret resource; the Setup cartridge writes the value. Apply doesn't re-roll the secret. |
| Tfvars snippet (display-only) | Configuration that genuinely belongs in version control (feature flags, regional toggles) | Cartridge generates the snippet and shows it to the operator | Operator copies the snippet into the repo's tfvars file, opens a PR, applies via CI. |
The Setup cartridge knows which pattern each flow uses and surfaces the right UX. For SSM and Secrets Manager patterns, "Save inputs" writes immediately. For the tfvars pattern, "Save inputs" generates a copy-pasteable snippet and a "Open PR with this change" link to GitHub.
SSO uses Pattern 1 (SSM). After Azure admin returns the metadata URL:
/forge/sso/azure-metadata-url in SSM./forge/sso/enabled = true in SSM.sso_enabled = true, applies the IdP resource.provisioned.verified.This is the small wiring change needed: console/sso.tf reads sso_enabled and sso_metadata_url from SSM data sources instead of via input variables. That makes the Setup cartridge's writes effective on next apply. The current PR 95/96 wiring uses input variables; PR 4 of this ship plan (section 10) updates it.
Standard cartridge shape. API Gateway HTTP API + Lambda + DDB + CloudFront-fronted S3 frontend.
cartridges/global/setup/
backend.tf Lambda + API Gateway + IAM
frontend.tf S3 + CloudFront
auth.tf Cognito client (admin group required)
cartridge.tf Manifest
lambda/
handler.js Routes for each flow
flows/
sso.js SSO flow handler (state machine)
dns.js DNS flow handler
salesforce.js Salesforce setup flow handler
...
state.js DDB state read/write
frontend/
index.html Cartridge home (flow list)
setup-sso.html SSO wizard
setup-dns.html DNS wizard
setup-salesforce.html Salesforce wizard
js/
wizard.js Shared wizard component (uses SDK Modal + Table)
copy-out.js Copy-out card behavior
paste-back.js Paste-back form behavior
content/ (until CMS lands)
sso-prereqs.md Step 6.p section 12 in markdown
dns-instructions.md DNS admin instructions
salesforce-howto.md Salesforce Connected App walkthrough
Table: forge-setup-state
pk: "flow"
sk: flow_id // e.g. "sso-azure", "dns-paycargo-org",
// "salesforce-sandbox", "salesforce-production"
Attributes:
status: // "not-started" | "inputs-displayed" | "handoff-sent" |
// "inputs-captured" | "provisioned" | "verified"
handoff_sent_by: // cognito username
handoff_sent_at: // ISO timestamp
inputs_captured_by: // cognito username
inputs_captured_at: // ISO timestamp
inputs: // JSON map of return values (non-secret)
provisioned_at: // ISO timestamp (when TF apply succeeded)
verified_at: // ISO timestamp
last_check_status: // "ok" | "failed" | "pending"
Proposed: SSM parameters (Pattern 1). Setup cartridge writes /forge/sso/enabled and /forge/sso/metadata-url. Terraform reads via data sources on next apply. Requires small change to console/sso.tf in PR 4 of the Setup cartridge ship plan.
Proposed: Cartridge triggers the existing Terraform Apply workflow via GitHub workflow_dispatch. Same gating as today (manual APPLY confirm). The cartridge passes the trigger; the human still types the confirmation in GitHub Actions UI.
Proposed: SSO + DNS + Salesforce Sandbox in v1. Salesforce Production, GitHub, GitBook, Monday in v1.5 as additional flows after the pattern is validated. Each new provider is one new flow file; cheap to add.
Proposed: files in the cartridge until CMS lands; CMS-served after. The Setup cartridge code v1.0 ships with markdown files. When the CMS cartridge (Step 6.s) is live, a small refactor swaps the read source.
Proposed: cartridges/global/setup/. Setup is a platform concern, not a department concern. Lives under global/ alongside the chatbot and the future Request Roles + CMS + MCP cartridges.
Proposed: per-flow automated check. For SSO: cartridge attempts a SAML test sign-in. For DNS: cartridge does a DNS lookup. For Salesforce: cartridge makes a test OAuth flow. Each flow declares its own verify function.
cartridges/global/setup/ with backend (Lambda + API Gateway + DDB + IAM), frontend (S3 + CloudFront), Cognito admin gate, manifest. Empty flow list (no flows implemented yet).
wizard.js, copy-out.js, paste-back.js. Built on SDK v1 components. Generic enough to be reused per flow.
flows/sso.js, setup-sso.html, content/sso-prereqs.md. Updates console/sso.tf to read from SSM data sources instead of input variables. End-to-end test: operator clicks through, writes SSM, triggers apply, verifies.
flows/dns.js + content. Reads from the domains module outputs; surfaces the 3 batches of records.
v1 ships after PR 7. Subsequent flows (Salesforce Production, GitHub, GitBook, Monday, etc.) are individual PRs adding one flows/<name>.js + content file + frontend page each.
PR 95/96 of Step 6.p declared SSO at the Console layer with input variables. PR 4 of this Setup cartridge ship plan refactors that to read from SSM data sources instead. Same end state; just rewired so the Setup cartridge can write the values. The PR 95/96 outputs (console_sso_acs_url, console_sso_entity_id) remain valid; the cartridge will read them too.