toga-ai 1.0.816 → 1.0.818

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ # store (Store) — 1.0 knowledge
2
+
3
+ | Doc | Summary |
4
+ |-----|---------|
5
+ | [Store (1.0 Storefront / SAML ACS) Architecture](architecture.md) | `store` is a framework **1.0** (`App_`) storefront app. |
6
+ | [Store SAML SP Certificate & Private Keys (togahub.com)](features/saml-sso-certificate-and-keys.md) | How the `store` repo's SAML Service Provider credential — the `togahub.com` X.509 cert and its RSA private key — is stored, loaded and rotated. |
@@ -0,0 +1,72 @@
1
+ ---
2
+ title: Store (1.0 Storefront / SAML ACS) Architecture
3
+ framework: "1.0"
4
+ repo: store
5
+ project: Store
6
+ client: shared
7
+ type: architecture
8
+ status: active
9
+ updated: 2026-09-15
10
+ owners: ["rgirish"]
11
+ files:
12
+ - store/_/app/framework.php
13
+ - store/config.production.ini
14
+ related:
15
+ - ../library/architecture.md
16
+ - ./features/saml-sso-certificate-and-keys.md
17
+ - ../../../clients/prudential/workflows/store-saml-deploy-topology.md
18
+ ---
19
+
20
+ ## Summary
21
+
22
+ `store` is a framework **1.0** (`App_`) storefront app. It also hosts the **SAML Assertion
23
+ Consumer Service (ACS)** for several clients on the `*.togahub.com` hostnames — the inbound
24
+ IdP assertion is decrypted **here**, not in the 2.0 `saml` gateway. SAML lives inline in
25
+ `store/_/app/framework.php` as a large `switch ($returnTo)` on the RelayState value, with one
26
+ inline `$settingsInfo` block per client and a `case 'meta' / case 'acs'` pair inside each.
27
+
28
+ The SP cert/key material lives in the sibling `library` repo under `library/ssl/toga/` and is
29
+ read from disk at request time via `__LIBROOT__`.
30
+
31
+ **Critical rules:** never paste private-key material into `framework.php` — read it from
32
+ `library/ssl/toga/*.key` with `file_get_contents`. The SAML ACS host and the storefront host for
33
+ a client can be **different EB environments**; check DNS before assuming a deploy target
34
+ (see the Prudential store SAML deploy-topology workflow). Certificate and private key must be
35
+ rotated **together** — the three inline SP blocks each carry an `x509cert` that has to match the
36
+ key file.
37
+
38
+ ## SAML entry points
39
+
40
+ Routing is by the RelayState value (`$_POST['RelayState']`, read as `$returnTo`), not by path:
41
+
42
+ | Sub-route | Purpose |
43
+ |---|---|
44
+ | `?meta` | SP metadata XML from `OneLogin_Saml2_Settings::getSPMetadata()` |
45
+ | `?acs` | Assertion Consumer Service — decrypts the inbound `SAMLResponse` |
46
+
47
+ Two SAML stacks are in play in the same file:
48
+ - **php-saml (`OneLogin_Saml2_*`)** — builds the SP settings + `?meta` metadata.
49
+ - **LightSAML + xmlseclibs** — does the actual `?acs` assertion decryption
50
+ (`decryptMultiAssertion`). See the SAML SP certificate & keys feature doc.
51
+
52
+ The RUMCSI branch (`rumcsi.togaview.agilantsolutions.com`) skips decryption entirely and parses
53
+ the raw assertion XML with `SimpleXMLElement` + XPath.
54
+
55
+ ## Handoff to the storefront
56
+
57
+ After a successful assertion, the Prudential-style branches base64-encode a plain
58
+ `xid:first:last:email` string and redirect to `<returnTo>?hash=<base64>`. This is **not
59
+ encrypted and not signed** — unlike the 2.0 gateway's `?saml=` encrypted + uuid-validated
60
+ handoff. See the "Client_True migration (deferred)" section of the SAML SP certificate & keys
61
+ feature doc.
62
+
63
+ ## Known issues
64
+
65
+ - `case 'meta'` sets the `Content-Type` header and `exit`s **without ever echoing
66
+ `$metadata`** — the `?meta` endpoint returns an empty body. Present in both `meta` blocks.
67
+ - `'debug' => true` is set on all three SAML `$settingsInfo` blocks, and a `debugVar()` call
68
+ dumps SAML attributes (XID, name, email) to output.
69
+ - `'strict'` is never set, so php-saml defaults it to `false` and skips condition, timestamp and
70
+ audience validation. Neither `wantAssertionsSigned` nor `wantMessagesSigned` is set.
71
+ - Prudential's SAML config is ~700 lines of inline `switch` in one file with no
72
+ `_Model_<Client>_ClientAuthentication` equivalent.
@@ -0,0 +1,188 @@
1
+ ---
2
+ title: Store SAML SP Certificate & Private Keys (togahub.com)
3
+ framework: "1.0"
4
+ repo: store
5
+ project: Store
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-09-15
10
+ owners: ["rgirish"]
11
+ files:
12
+ - store/_/app/framework.php
13
+ - library/ssl/toga/togahub_com_2027-03-31.crt
14
+ - library/ssl/toga/toga_private_key_2027-03-31.key
15
+ related:
16
+ - ../architecture.md
17
+ - ../../library/features/app-sso-initiation.md
18
+ - ../../../2.0/apps/saml/workflows/rotating-the-saml-sp-certificate.md
19
+ - ../../../2.0/standards/ssl-certificate-trust-model.md
20
+ - ../../../clients/prudential/workflows/store-saml-deploy-topology.md
21
+ ---
22
+
23
+ ## Summary
24
+
25
+ How the `store` repo's SAML Service Provider credential — the `togahub.com` X.509 cert and its
26
+ RSA private key — is stored, loaded and rotated. `store` decrypts inbound IdP assertions itself
27
+ (LightSAML), so this credential is what makes Prudential/RUMCSI SSO work. It is **separate from**
28
+ the 2.0 `saml` gateway's SP credential — rotating one does not rotate the other. (As of
29
+ 2026-09-15 the gateway's `/acs` loads its decrypt certs from AWS Secrets Manager, not from disk;
30
+ `store` still reads files. See the SP cert-rotation workflow.)
31
+
32
+ The cert and key files live in the sibling `library` repo at `library/ssl/toga/` and are read at
33
+ request time through `__LIBROOT__`.
34
+
35
+ ## How it works
36
+
37
+ ### Where the material lives (never paste values)
38
+
39
+ - **AWS Secrets Manager**, account **654654170868**, region **us-east-1**.
40
+ - Secret: **`toga/ssl/togahub_com-casigned-2027-03-31`**.
41
+ - JSON keys: `certificate`, `privateKey`, `certificateChain`, `passphrase`.
42
+ - On disk (deployed with the `library` repo):
43
+ - `library/ssl/toga/togahub_com_2027-03-31.crt` — active cert
44
+ - `library/ssl/toga/togahub_com_2027-03-31-chain.crt` — chain
45
+ - `library/ssl/toga/toga_private_key_2027-03-31.key` — active key, **stored decrypted**, mode `600`
46
+ - `library/ssl/toga/togahub_com_2026-02-27.crt` + `toga_private_key.key` — previous pair,
47
+ kept only as the rotation fallback
48
+
49
+ ### Current certificate (rotated 2026-09-15)
50
+
51
+ | | Old | New |
52
+ |---|---|---|
53
+ | File | `togahub_com_2026-02-27.crt` | `togahub_com_2027-03-31.crt` |
54
+ | Issuer | DigiCert / Thawte | Amazon (ACM, `Amazon RSA 2048 M01`) |
55
+ | Validity | expired **2026-09-16** | **2026-09-15 → 2027-03-31** |
56
+ | SANs | 14 | **2** — `togahub.com`, `*.togahub.com` |
57
+
58
+ **The new cert covers only `togahub.com`.** The six other domains the old cert carried
59
+ (`togacommerce`, `togadesk`, `togaview`, `togasupply`, `togaretail`, `togaiq`, plus their
60
+ wildcards) are **not** on it — each of those needs its own ACM cert on its own listener.
61
+
62
+ ### Dual-keypair decrypt (rotation overlap)
63
+
64
+ `case 'acs'` in `store/_/app/framework.php` builds a `$samlKeyPairs` array — **active keypair
65
+ first, previous keypair second** — then one `LightSaml\Credential\X509Credential` per readable
66
+ pair, and passes the whole list to
67
+ `$reader->decryptMultiAssertion($credentials, $decryptDeserializeContext)`.
68
+
69
+ LightSAML's `decryptMulti()` loops the credentials, swallows each individual failure and returns
70
+ on the first success, re-throwing only when all fail. So **no custom retry logic is needed** and
71
+ it fails closed. An IdP still encrypting to the old cert and one already on the new cert both
72
+ work during the overlap. Unreadable keypairs are skipped with `error_log`; an empty credential
73
+ list throws `RuntimeException`.
74
+
75
+ This mirrors the overlap the 2.0 `saml` gateway provides, though the gateway now does it with
76
+ a multi-cert list out of Secrets Manager rather than two files on disk — see the SP
77
+ cert-rotation workflow.
78
+
79
+ ### Two ACS handlers, two SAML libraries — tell them apart from the stack trace
80
+
81
+ `store/_/app/framework.php` has **two** separate `case 'acs'` handlers that use **different SAML
82
+ libraries**. They are easy to confuse when reading a production stack trace, and they run on
83
+ **different servers**:
84
+
85
+ | Handler | Library | Host / EB environment |
86
+ |---|---|---|
87
+ | The `togahub` host block's `case 'acs'` (the `$samlKeyPairs` dual-keypair decrypt) | **LightSAML** (`decryptMultiAssertion`) | `prudential.togahub.com` → `prudential-store-prod` |
88
+ | The `prudential` host block's `case 'acs'` | **OneLogin_Saml2_Auth** (`isAuthenticated()`, `getNameId()`, `getAttribute('givenname'/'surname'/'emailaddress')`) | `prudential.togacommerce.com` → `true-togacommerce-prod` |
89
+
90
+ **Diagnostic rule:** a trace with `LightSaml\Model\Assertion\*` frames is the **togahub** block
91
+ (`prudential-store-prod`). A trace with `OneLogin` frames is the **prudential** block
92
+ (`true-togacommerce-prod`). This is how a production decrypt error gets pinned to the right
93
+ server — and the two are deployed by different pipeline actions, so fixing the wrong one changes
94
+ nothing. See the Prudential store SAML deploy-topology workflow.
95
+
96
+ ### Private key loading
97
+
98
+ The three inline `$settingsInfo` SP blocks read the key from disk:
99
+
100
+ ```php
101
+ 'privateKey' => trim(file_get_contents(__LIBROOT__ . '/ssl/toga/toga_private_key_2027-03-31.key')),
102
+ ```
103
+
104
+ The key file must be **decrypted on disk** because `framework.php` calls
105
+ `KeyHelper::createPrivateKey($path, '', true)` with an empty password — an encrypted PEM will not
106
+ load.
107
+
108
+ **Deploy model:** the `ssl/**/*.key` ignore rule was deliberately removed from
109
+ `library/.gitignore` so the key ships with the repo. That is the team's current model for this
110
+ key; it is a record of what we do, not a recommendation.
111
+
112
+ ## Gotchas
113
+
114
+ - **PEM newlines are stored as SPACES in Secrets Manager.** The `certificate` / `privateKey`
115
+ values in `toga/ssl/togahub_com-casigned-2027-03-31` have their line breaks replaced by spaces.
116
+ `openssl` rejects the value straight out of the secret. Re-wrap the base64 body at 64 chars
117
+ before writing it to disk.
118
+ - **The date in a secret name is the EXPIRY date, not the issue date.** The secret
119
+ `toga/ssl/togahub_com-casigned-2026-09-16` is **not** a newer cert — it holds the cert that was
120
+ already expiring. Read the dates out of the cert itself, never off the secret name.
121
+ - **Rotate the cert and the key together.** Before 2026-09-15 the three SP blocks had the raw
122
+ private key pasted inline as a base64 string, and the `x509cert` values had been rotated while
123
+ those inline keys had not — cert and key no longer matched, which would have broken SSO on the
124
+ next deploy. Keys are now read from a file, so they cannot drift apart silently.
125
+ - **Don't assume where the ACS runs.** The host that terminates `?acs` for a client may be a
126
+ different EB environment from the storefront. See the Prudential store SAML deploy-topology
127
+ workflow before deploying a cert change.
128
+ - **A decrypt stack trace CANNOT tell you how many keys were tried.** In
129
+ `library/LightSaml/Model/Assertion/EncryptedElementReader.php`, `decryptMulti()` returns
130
+ `$this->decrypt($key)` from *inside* the `foreach`, and re-throws `$lastException` after the
131
+ loop. The re-thrown exception keeps the stack from where it was first thrown, so the captured
132
+ trace looks **identical** whether one key was tried or several all failed. Seeing
133
+ `decryptMultiAssertion` in a trace therefore does **not** prove the dual-keypair code is
134
+ deployed — the old single-key code called the same method with a one-item array. To tell them
135
+ apart, check the deployed EB `Deployment.VersionLabel`, or look for the new code's distinct log
136
+ line `SAML keypair not readable, skipping:`.
137
+ - **A mismatched cert/key pair fails with an OAEP padding error, not a clear message.** The
138
+ symptom is `Failure decrypting Data (openssl private) -
139
+ RSA_padding_check_PKCS1_OAEP_mgf1: oaep decoding error` (also seen as
140
+ `RSA_EAY_PRIVATE_DECRYPT: padding check failed`) thrown out of the LightSAML
141
+ `EncryptedElementReader`. Confirm a suspected mismatch by comparing the **modulus** of the cert
142
+ and the key with `openssl x509 -noout -modulus` / `openssl rsa -noout -modulus` — identical
143
+ modulus means the pair matches. Compare the **committed git blobs**, not just the local working
144
+ files, or you will verify a pair that was never deployed.
145
+
146
+ ## Client_True migration (deferred 2026-09-15)
147
+
148
+ Moving Prudential SAML onto the `Client_True` pattern (config `[saml]` section +
149
+ `Core.ClientAuthentications` / `ClientAuthentications_Sso` rows + `App_Sso` + a
150
+ `_Model_<Client>_ClientAuthentication`) was reviewed and **deferred to its own ticket** — the cert
151
+ expired the next day and the migration is multi-day work.
152
+
153
+ Reference implementation to copy when it is picked up: `tools/config.production.ini` `[saml]`
154
+ section, `library/app/sso.php` (`App_Sso::initiate`), `tools/mvc/sso/initiate/get.php`,
155
+ `tools/mvc/sso/get.php`.
156
+
157
+ What the migration must handle:
158
+ - Prudential today has ~700 lines of inline `switch` keyed on `$returnTo`, with certs pasted into
159
+ PHP, and no `_Model_Prudential_ClientAuthentication`.
160
+ - Its handoff is a plain-base64 `?hash=` of `xid:first:last:email` — unencrypted and forgeable —
161
+ versus the encrypted, uuid-validated `?saml=` handoff `Client_True` uses.
162
+ - Prudential **auto-creates users on first login**. `Client_True`'s `getAuthenticatedSsoUser()`
163
+ does not, so that behavior must be written in or SSO logins will start failing for new users.
164
+
165
+ ## Open security items (not fixed 2026-09-15)
166
+
167
+ - The old private key is still in git history — `library` commits `6addc58f7` and `29d55bb97`,
168
+ plus the `store` `framework.php` history. Tracked as a separate compromised-credential ticket.
169
+ - `'debug' => true`, missing `'strict'`, and the attribute-dumping `debugVar()` — see the store
170
+ architecture doc's Known issues.
171
+
172
+ ## Change history
173
+ - 2026-09-15 — Documented that `framework.php` has two `case 'acs'` handlers on two different
174
+ SAML libraries (LightSAML for `togahub` vs OneLogin for `prudential`) running on two different
175
+ EB environments, and how to read a stack trace to tell which one failed. Recorded that a
176
+ LightSAML `decryptMulti()` trace cannot reveal how many keys were tried (re-thrown exception
177
+ keeps the original stack), so `decryptMultiAssertion` in a trace does not prove the
178
+ dual-keypair build is deployed — check the EB VersionLabel or the
179
+ `SAML keypair not readable, skipping:` log line. Added the OAEP-padding-error signature of a
180
+ mismatched cert/key pair and the openssl-modulus check against committed blobs. (rgirish)
181
+ - 2026-09-15 — Rotated the `togahub.com` SP cert to the ACM-issued `togahub_com_2027-03-31.crt`
182
+ and added a dual-keypair fallback in `?acs` so both the old and new certs decrypt during the
183
+ overlap (`decryptMultiAssertion`). Removed three hardcoded RSA private-key blobs from
184
+ `store/_/app/framework.php` and replaced them with `file_get_contents` on
185
+ `library/ssl/toga/toga_private_key_2027-03-31.key`; chmod 600 the old world-readable key.
186
+ Recorded the Secrets Manager location, the space-instead-of-newline PEM gotcha, and that the
187
+ new cert carries only 2 SANs. Deferred the `Client_True` SAML migration. Commit `3839e49a`.
188
+ (rgirish)
@@ -14,6 +14,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
14
14
  - **walmarttechservices** (Walmart Tech Services) — 1 doc(s) → [1.0/apps/walmarttechservices/INDEX.md](1.0/apps/walmarttechservices/INDEX.md)
15
15
  - **test** (Test) — 16 doc(s) → [1.0/apps/test/INDEX.md](1.0/apps/test/INDEX.md)
16
16
  - **toga** (TOGa) — 3 doc(s) → [1.0/apps/toga/INDEX.md](1.0/apps/toga/INDEX.md)
17
+ - **store** (Store) — 2 doc(s) → [1.0/apps/store/INDEX.md](1.0/apps/store/INDEX.md)
17
18
  - **tools** (Tools) — 18 doc(s) → [1.0/apps/tools/INDEX.md](1.0/apps/tools/INDEX.md)
18
19
 
19
20
  ## 2.0 framework
@@ -16,3 +16,4 @@
16
16
  | [Prudential Financial](profile.md) | 2.0 | Prudential is a TOGA client whose device-fulfillment flow is driven by **Dell** via the Dell API (`Client_Prudential.Apis.id = 2`). |
17
17
  | [Prudential: Dell ASN failed POST backfill replay](workflows/dell-asn-backfill-replay.md) | 2.0 | When Dell ASN POSTs fail in bulk (e.g. |
18
18
  | [Prudential — HTTP/2 disabled on the Commerce ALB (proxy workaround) + open re-enable action](workflows/http2-alb-workaround.md) | 2.0 | Prudential users hit **`ERR_HTTP2_PROTOCOL_ERROR`** in Chrome and Edge (both Chromium) loading `https://prudential.togacommerce.com`, while the site worked fine |
19
+ | [Prudential store/SAML Deploy Topology & the Wedged-Agent Deploy Timeout](workflows/store-saml-deploy-topology.md) | 1.0 | Where Prudential's SAML ACS actually runs versus where its storefront runs — they are **two different Elastic Beanstalk environments**, and the CodePipeline act |
@@ -12,6 +12,7 @@ apps:
12
12
  - toga2-commerce
13
13
  - toga25-supply
14
14
  - saml
15
+ - store
15
16
  project: _Underscore
16
17
  client: prudential
17
18
  type: profile
@@ -33,6 +34,7 @@ related:
33
34
  - features/supply-orders-csv-export.md
34
35
  - features/order-email-pdf-attachments.md
35
36
  - features/itemclasses-device-tier-data.md
37
+ - workflows/store-saml-deploy-topology.md
36
38
  ---
37
39
 
38
40
  ## Summary
@@ -80,6 +82,13 @@ The order-created cron branches on this id; the shipped/delivered crons branch o
80
82
  breaks HTTP/2, causing `ERR_HTTP2_PROTOCOL_ERROR` in Chrome/Edge. HTTP/2 is currently disabled
81
83
  globally on the `true-togacommerce-prod` ALB as a workaround — **open action to re-enable once
82
84
  their IT fixes the proxy.** See workflows/http2-alb-workaround.md.
85
+ - **SAML ACS and the storefront are DIFFERENT EB environments.** `prudential.togahub.com` (where
86
+ the SAML assertion is decrypted, in the 1.0 `store` repo) points at `prudential-store-prod`
87
+ (us-west-2, account 502614707982), deployed by the pipeline action **`PrudentialStore`**.
88
+ `prudential.togacommerce.com` (the storefront) points at `true-togacommerce-prod` and is
89
+ deployed by **`TrueProductionDeployment`** — so the action named "PrudentialStore" does *not*
90
+ deploy the storefront. `rumcsi.togahub.com` shares `prudential-store-prod`. See
91
+ workflows/store-saml-deploy-topology.md.
83
92
  - Dell will not change their payload shape — see the Dell ASN units interceptor feature doc for the
84
93
  PRE/POST translation that keeps their feed working after the tracking-number bridge migration.
85
94
  - Service request region is resolved from `Customers.name` string (`'USA'`/`'India'`/`'Ireland'`).
@@ -117,6 +126,10 @@ The order-created cron branches on this id; the shipped/delivered crons branch o
117
126
  linking, the "null null" contact-export incident + dedup/backfill remediation.
118
127
  - features/order-email-pdf-attachments.md — which PDF guide is attached to which customer
119
128
  order email at which stage, the S3 asset list, and the four crons that send them.
129
+ - workflows/store-saml-deploy-topology.md — where Prudential's SAML ACS actually runs vs
130
+ the storefront, the split AWS accounts, and the wedged-agent EB deploy timeout.
131
+ - ../../1.0/apps/store/features/saml-sso-certificate-and-keys.md — the togahub.com SP cert/key
132
+ that decrypts Prudential's assertions.
120
133
  - features/supply-orders-csv-export.md — the Prudential-only Sales Orders CSV export in TOGa 2.5
121
134
  Supply (`toga25-supply`): its two queries, why contacts join `SalesOrders.contactId` (not
122
135
  `Units.contactId`, NULL until Dell assigns the asset), and the `ServiceRequests_Units` fan-out.
@@ -0,0 +1,157 @@
1
+ ---
2
+ title: Prudential store/SAML Deploy Topology & the Wedged-Agent Deploy Timeout
3
+ framework: "1.0"
4
+ project: Store
5
+ client: prudential
6
+ type: workflow
7
+ status: active
8
+ updated: 2026-09-15
9
+ owners: ["rgirish"]
10
+ files:
11
+ - store/_/app/framework.php
12
+ related:
13
+ - ../profile.md
14
+ - ../../../1.0/apps/store/architecture.md
15
+ - ../../../1.0/apps/store/features/saml-sso-certificate-and-keys.md
16
+ ---
17
+
18
+ ## Summary
19
+
20
+ Where Prudential's SAML ACS actually runs versus where its storefront runs — they are **two
21
+ different Elastic Beanstalk environments**, and the CodePipeline action names point at the
22
+ opposite one from what you would guess. Read this before deploying anything SAML-related for
23
+ Prudential. It also records the EB deploy failure mode we hit on 2026-09-15 (a wedged command
24
+ agent that times out after 14 minutes while the environment still shows Green).
25
+
26
+ ## The full login flow — the two legs run on two different servers
27
+
28
+ The outbound (first) leg does **not** hop through togahub. It redirects the browser straight to
29
+ Microsoft Entra:
30
+
31
+ 1. User hits `prudential.togacommerce.com` (**`true-togacommerce-prod`**). The `prudential`
32
+ host block in `store/_/app/framework.php` fires when the session host is `prudential`, the app
33
+ is not in test mode, and the session has no `accountId`.
34
+ 2. With no recognized `?ssoCommand`, it falls to the `default:` case, which calls
35
+ `$auth->login($originUrl)` on `OneLogin_Saml2_Auth`.
36
+ 3. That redirects to Prudential's Entra tenant:
37
+ `https://login.microsoftonline.com/d8fde2f5-9392-4260-8a03-0ad01f4746e9/saml2`.
38
+ 4. The SP block advertises entityId `https://prudential.togahub.com/?meta` and ACS
39
+ `https://prudential.togahub.com/?acs` — so **Entra POSTs the response to togahub**, i.e. the
40
+ inbound leg lands on **`prudential-store-prod`**, a different EB environment.
41
+ 5. `prudential-store-prod` decrypts the assertion (LightSAML), then redirects back to
42
+ `prudential.togacommerce.com/?hash=<base64 of xid:first:last:email>`.
43
+
44
+ **Why this matters:** the outbound and inbound legs are different servers with different
45
+ deploys. A deploy that succeeds on one does **not** fix the other. Decrypt/cert changes belong on
46
+ `prudential-store-prod`.
47
+
48
+ ## The routing — the pipeline action names are misleading
49
+
50
+ | Hostname | CNAME → EB environment | Deployed by pipeline action |
51
+ |---|---|---|
52
+ | `prudential.togacommerce.com` (storefront) | `true-togacommerce-prod.us-west-2` | **`TrueProductionDeployment`** |
53
+ | `prudential.togahub.com` (**SAML ACS**) | `prudential-store-prod.us-west-2` | **`PrudentialStore`** |
54
+ | `rumcsi.togahub.com` | `prudential-store-prod.us-west-2` | `PrudentialStore` |
55
+
56
+ **The action named `PrudentialStore` does NOT deploy the Prudential storefront.**
57
+ `TrueProductionDeployment` does. `PrudentialStore` deploys the host that decrypts the SAML
58
+ assertion — so **cert / decrypt code changes must land on `prudential-store-prod`.**
59
+
60
+ `rumcsi.togahub.com` shares that same environment, so a bad SAML deploy there breaks RUMCSI
61
+ login too.
62
+
63
+ ### Accounts and regions (they are split)
64
+
65
+ - **Pipeline:** `Store-Production`, AWS account **502614707982**, region **us-west-2**.
66
+ Not `us-east-1`, and not account `654654170868` where most other TOGA pipelines live.
67
+ - **Route53** zones for `togacommerce.com` and `togahub.com` are in account **654654170868**,
68
+ while the EB environments they point at are in **502614707982**. You need both accounts to
69
+ trace a hostname end-to-end.
70
+
71
+ ## Failure mode — 14-minute command timeout with a Green environment
72
+
73
+ Symptoms in the EB event log:
74
+
75
+ ```
76
+ The following instances have not responded in the allowed command timeout time
77
+ Command execution completed on all instances. Summary: [Successful: 0, TimedOut: 1]
78
+ Failed to deploy application.
79
+ ```
80
+
81
+ The environment still reports **Green / Ok**, which is misleading — the deploy never ran, but
82
+ the old version is still serving traffic fine.
83
+
84
+ ### How to confirm it is a wedged command agent
85
+
86
+ 1. Check what the instance actually thinks it is running:
87
+ ```
88
+ aws elasticbeanstalk describe-instances-health --attribute-names All \
89
+ --environment-name prudential-store-prod --region us-west-2
90
+ ```
91
+ A wedged agent shows `Deployment.VersionLabel` still on the **old** build with
92
+ `Deployment.Status: Deployed`, while `CPU Idle` is ~99% and load average ~0.0. That means the
93
+ instance never picked up the deploy command at all — it is not busy, it is deaf.
94
+ 2. Check whether SSM sees the instance:
95
+ ```
96
+ aws ssm describe-instance-information --region us-west-2
97
+ ```
98
+ An **empty** result for the instance confirms the agent is not reporting.
99
+
100
+ Observed 2026-09-15: `prudential-store-prod` on `i-07ae1b81ee48df938` (t3.micro, running since
101
+ 2026-07-17), stuck on an Aug 13 build. `spglobal-store-prod` failed identically on
102
+ `i-04a6a50cffd37457e` — that environment has **no instances and no DNS pointing at it**.
103
+
104
+ ## What you need to do
105
+
106
+ 1. Confirm the diagnosis with the two AWS commands above before touching anything.
107
+ 2. Reboot the EC2 instance (EC2 console or `aws ec2 reboot-instances`).
108
+ 3. Watch the EB event log for `Command is executing on all instances` — that means the EB command
109
+ channel is back.
110
+ 4. Re-run the failed pipeline deploy.
111
+ 5. If it times out again, do **not** reboot a second time. Use an EB **Instance refresh** /
112
+ instance replacement instead.
113
+
114
+ A reboot restored the EB command channel on 2026-09-15, but SSM still did not report after 3
115
+ minutes — EB working again is the signal to retry, not SSM.
116
+
117
+ ## Incident 2026-09-15 — OAEP decrypt error caused by the stuck deploy
118
+
119
+ Production Sentry showed, from the togahub (LightSAML) ACS handler:
120
+
121
+ ```
122
+ Failure decrypting Data (openssl private) - RSA_padding_check_PKCS1_OAEP_mgf1: oaep decoding error
123
+ ```
124
+ (also seen as `RSA_EAY_PRIVATE_DECRYPT: padding check failed`)
125
+
126
+ **Root cause: not a bad key — a deploy that never landed.** `prudential-store-prod` was still
127
+ running the pre-rotation code (commit `275d16a9`, an Aug 13 build) because its deploy kept timing
128
+ out with the wedged-agent failure above. That old code loaded a **mismatched cert/key pair**: the
129
+ `togahub_com_2025-03-25.crt` cert with `toga_private_key.key`, which belongs to the 2026-02-27
130
+ cert. Confirmed by comparing the two openssl moduli — they differ. The new dual-keypair fallback
131
+ fixes this, but it was not on the box.
132
+
133
+ The `library` repo was verified **clean and not the cause**: all three new files committed and
134
+ pushed on `_production`, both keypairs (2027 active and 2026-02-27 fallback) verified as matching
135
+ pairs against the **committed git blobs** (not just local files), both keys unencrypted (no
136
+ `Proc-Type` / `DEK-Info`), and both loading via the exact call the code makes —
137
+ `openssl_pkey_get_private($pem, "")`.
138
+
139
+ **Lesson:** when a SAML decrypt error appears right after a cert rotation, check *what build is
140
+ actually deployed* before you suspect the key material. Verify against committed blobs, and note
141
+ that the stack trace alone cannot tell you which build is running (see the cert & keys feature
142
+ doc's gotcha on `decryptMulti`).
143
+
144
+ ## Change history
145
+ - 2026-09-15 — Traced the outbound SAML leg: `prudential.togacommerce.com` redirects the browser
146
+ straight to Prudential's Entra tenant (no togahub hop), and Entra POSTs back to
147
+ `prudential.togahub.com/?acs` on a different EB environment — so a deploy to one leg does not
148
+ fix the other. Recorded the OAEP decrypt incident: the error came from `prudential-store-prod`
149
+ still serving the pre-rotation Aug 13 build (`275d16a9`) with a mismatched cert/key pair,
150
+ because its deploy kept timing out; the `library` repo was verified clean against committed
151
+ git blobs. (rgirish)
152
+ - 2026-09-15 — Created while rotating the `togahub.com` SAML cert. Documented that
153
+ `prudential.togahub.com` (SAML ACS) and `prudential.togacommerce.com` (storefront) are separate
154
+ EB environments with swapped-sounding pipeline action names, the split AWS accounts/regions,
155
+ and the wedged-command-agent deploy timeout with its `describe-instances-health` /
156
+ `describe-instance-information` diagnosis and reboot-then-instance-refresh remediation.
157
+ (rgirish)
@@ -5,12 +5,13 @@ apps:
5
5
  - togadesk
6
6
  - togaview
7
7
  - library
8
+ - store
8
9
  project: TOGa Desk
9
10
  client: rumcsi
10
11
  type: profile
11
12
  status: active
12
- updated: 2026-08-21
13
- owners: ["mhammontree"]
13
+ updated: 2026-09-15
14
+ owners: ["mhammontree", "rgirish"]
14
15
  files: []
15
16
  related:
16
17
  - ../../1.0/apps/togadesk/features/per-client-host-restriction.md
@@ -19,6 +20,7 @@ related:
19
20
  - ../../1.0/apps/togadesk/features/email-to-ticket-intake.md
20
21
  - ../../1.0/apps/togadesk/architecture.md
21
22
  - ../../1.0/apps/togaview/architecture.md
23
+ - ../../1.0/apps/store/features/saml-sso-certificate-and-keys.md
22
24
  ---
23
25
 
24
26
  ## Summary
@@ -124,6 +126,12 @@ hardcoded to `clientId = 2`; no RUMC row has `adPeopleId`/`ldap_user`), and
124
126
  - **No SAML auto-provisioning.** Unlike towfoundation and newcenturyholdingsllc, the rumcsi
125
127
  branch only `SELECT`s `people`; it never creates them. That is precisely why the bulk load was
126
128
  necessary.
129
+ - **`rumcsi.togahub.com` runs on the `prudential-store-prod` EB environment** (1.0 `store` repo,
130
+ us-west-2, account 502614707982) — the same host as Prudential's SAML ACS. A bad SAML/cert
131
+ deploy there breaks RUMCSI login too. RUMCSI's ACS branch skips assertion decryption (raw
132
+ `SimpleXMLElement` + XPath), so the SP cert rotation does not affect it directly. See
133
+ ../../1.0/apps/store/features/saml-sso-certificate-and-keys.md and
134
+ ../prudential/workflows/store-saml-deploy-topology.md.
127
135
 
128
136
  ## Change history
129
137
 
@@ -211,6 +211,13 @@
211
211
  "role": "app",
212
212
  "dependsOn": []
213
213
  },
214
+ {
215
+ "repo": "store",
216
+ "project": "Store",
217
+ "framework": "1.0",
218
+ "role": "app",
219
+ "dependsOn": []
220
+ },
214
221
  {
215
222
  "repo": "toga-blox",
216
223
  "project": "TOGa Blox",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.816",
3
+ "version": "1.0.818",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",