toga-ai 1.0.201 → 1.0.202

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.
@@ -6,6 +6,7 @@
6
6
  | [Diagnostic Dialog — View Recommended Services Routing](features/diagnostic-dialog-view-recommended-services.md) | `App_Model_Toga_Diagnostic::initializeDiagnosticDialog()` renders the device modal used across all TOGa service request views. | library/app/model/toga/diagnostic.php |
7
7
  | [Elite Freshservice Sync (library)](features/elite-freshservice-sync.md) | `App_Api_Toga2` in `library/app/api/toga2.php` orchestrates bidirectional sync between TOGA 2 and TOGaDesk. | library/app/api/toga2.php |
8
8
  | [Branded HTML Email Templates (App_Email_Template)](features/email-templates.md) | `App_Email_Template` (`app/email/template.php`) is the base class for branded HTML emails in the 1.0 (`App_`) framework. | library/app/email/template.php, library/app/email/agilant.php |
9
+ | [1.0 MVC Page Pattern & New-App Skeleton](features/mvc-page-pattern-and-app-skeleton.md) | This is the **reusable recipe for standing up a new 1.0 (`App_`) application** and for adding pages to one — the folder-based MVC routing, the page lifecycle, t | library/app/framework.php, library/app/frameworkindex.php, library/app/mvc.php, library/app/database.php, library/app/model.php, library/app/config.php |
9
10
  | [NetSuite SuiteQL/REST API Reference](features/netsuite-suiteql-api-reference.md) | General working reference for the Agilant NetSuite integration: how to authenticate, how SuiteQL behaves, and the confirmed schema of the tables/columns/codes w | library/app/api/netsuite/rest.php, library/ssl/netsuite_ec_key.pem |
10
11
  | [NetSuite SuiteQL/REST Shim — Field Semantics](features/netsuite-suiteql-rest-shim.md) | `App_Api_Netsuite_Rest` is the REST/SuiteQL replacement for the deprecated NetSuite SOAP toolkit. | library/app/api/netsuite/rest.php |
11
12
  | [Startech PC Matic B2B Sync (library)](features/startech-pcmaticb2b-sync.md) | `library/app/api/toga2.php` handles bidirectional ticket sync for PC Matic B2B between TOGaDesk 1.0 and TOGA 2.0. | library/app/api/toga2.php |
@@ -0,0 +1,113 @@
1
+ ---
2
+ title: 1.0 MVC Page Pattern & New-App Skeleton
3
+ framework: "1.0"
4
+ repo: library
5
+ project: Library
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-06-25
10
+ owners: [jcardinal]
11
+ files:
12
+ - library/app/framework.php
13
+ - library/app/frameworkindex.php
14
+ - library/app/mvc.php
15
+ - library/app/database.php
16
+ - library/app/model.php
17
+ - library/app/config.php
18
+ related:
19
+ - ../architecture.md
20
+ - ../../togaview/architecture.md
21
+ ---
22
+
23
+ ## Summary
24
+
25
+ This is the **reusable recipe for standing up a new 1.0 (`App_`) application** and for adding
26
+ pages to one — the folder-based MVC routing, the page lifecycle, the model/DB conventions, and
27
+ the minimal new-app skeleton. The `library` core supplies all of it; an app contributes only its
28
+ `index.php`, config, `_/app/` framework subclasses, `mvc/` pages, and `common/` templates.
29
+ togaview is the reference implementation (see its architecture doc for the multi-tenant
30
+ specifics); this doc captures the framework-level pattern that any new 1.0 app reuses.
31
+
32
+ ## How it works
33
+
34
+ ### Folder-based, classless routing
35
+ A URL maps directly to a file on disk — **the file path IS the route**, there is no routing
36
+ config and no controller classes. `App_MVC::parseRoute()` turns the request URI into
37
+ `mvc/<folder…>/<httpmethod>.php`, matching **longest-to-shortest** so overlapping paths resolve
38
+ to the most specific folder.
39
+
40
+ ```
41
+ GET /task_list/ajax -> mvc/task_list/ajax/get.php
42
+ POST /login -> mvc/login/post.php
43
+ ```
44
+
45
+ - `<httpmethod>` is `get` or `post`, taken from the HTTP verb (overridable via a `_method` POST param).
46
+ - `get.php` renders a page; `post.php` does writes then redirects via `App_MVC::routeTo()`
47
+ (`header('Location: …'); exit;`).
48
+ - Because matching is longest-to-shortest, keep route folders distinct to avoid surprise matches.
49
+
50
+ ### Entry point (3 lines)
51
+ ```php
52
+ require '_.php'; // bootstraps library core + autoloader
53
+ App_Framework_<App>::initialize(); // env, config, DB links, session, Sentry
54
+ App_Framework_<App>::renderIndex(); // resolves stylePath + dispatches the page
55
+ ```
56
+ App-specific framework classes live in `<app>/_/app/` and extend the library bases
57
+ `App_Framework` (`framework.php`) and `App_FrameworkIndex` (`frameworkindex.php`).
58
+
59
+ ### Page lifecycle (no template engine)
60
+ `App_FrameworkIndex::body()` loads the matched mvc file **inline** between
61
+ `common/<stylePath>/header.php` and `common/<stylePath>/footer.php`. There is no master-template
62
+ engine — pages are inline PHP + HTML. `stylePath` is selected by `$_SERVER['HTTP_HOST']` in
63
+ `framework.php::renderIndex()`, giving per-brand CSS under `assets/css/<stylePath>/`. Pages guard
64
+ auth themselves by checking `$_SESSION` at the top.
65
+
66
+ ### Adding a page
67
+ Create `mvc/<page>/get.php` (and `post.php` if it writes). Scaffold from `mvc/_TEMPLATE/`.
68
+
69
+ ### Models
70
+ Extend `App_Model`, named `App_Model_<Db>_<Table>`, declaring `const TABLENAME`, `const DATABASE`,
71
+ and `public $field = self::FIELDTYPE_*` for each column. Field types: `PRIMARYKEY`, `INT`, `CHAR`,
72
+ `DATETIME`, `BLOB`, `BLOBSTORAGE`, `FOREIGNKEY`, `SERIALIZED`, `DATETIME_CREATED`,
73
+ `DATETIME_UPDATED`.
74
+
75
+ ```php
76
+ class App_Model_Toga_Client extends App_Model {
77
+ const TABLENAME = 'Clients';
78
+ const DATABASE = 'db_toga';
79
+ public $id = self::FIELDTYPE_PRIMARYKEY;
80
+ public $clientName = self::FIELDTYPE_CHAR;
81
+ }
82
+ ```
83
+ Usage: `new App_Model_X($id)` loads a row; `->save()` persists; `App_Model_X::lookupValueByField()`.
84
+
85
+ ### Databases (multiple are first-class)
86
+ DB access is mysqli via `App_Database::query($sql, 'db_<name>')`. **Multiple databases are
87
+ first-class:** each `[database_<name>]` section in `config.<env>.ini` is registered as registry
88
+ key `db_<name>` by `App_Database::registerDatabaseConnect()`. SELECTs **auto-route to
89
+ `<link>_read`** when the host is an RDS cluster (read-cluster support). Escape with
90
+ `App_Database::sqlEscape()`; read results with `buildArrayOfRows()` / `fetchRow()`.
91
+
92
+ ### Config
93
+ `App_Config` does `parse_ini_file(config.<ENVIRONMENT>.ini)`; the environment comes from
94
+ `getenv('ENVIRONMENT')` (defaults to `'worker'` — an unset env loads the wrong file). Sections:
95
+ `[internal]`, `[database_*]`, `[email]`, `[external]`, `[modules]`, `[api]`.
96
+
97
+ > **Secrets are currently stored in plaintext ini files** — a known practice, not a recommended
98
+ > one. Document where a credential lives, never its value.
99
+
100
+ ## Minimal new-app skeleton
101
+ ```
102
+ index.php # the 3-line entry point
103
+ config.<env>.ini # per-environment config + DB sections
104
+ mvc/_TEMPLATE/get.php # page scaffold
105
+ mvc/<page>/{get,post}.php # one folder per route
106
+ common/<stylePath>/{header,footer}.php # brand shell wrapped around each page
107
+ common/404.php
108
+ assets/ # css/js per stylePath
109
+ ```
110
+ The app depends on the `library` core repo (bootstrapped via `_.php`).
111
+
112
+ ## Change history
113
+ - 2026-06-25 — Documented the 1.0 folder-based MVC page pattern, model/multi-DB conventions, and the minimal new-app skeleton, discovered while planning the new "Toolbox" 1.0 app (reference: togaview) (jcardinal)
@@ -3,4 +3,5 @@
3
3
  | Doc | Summary | Files |
4
4
  |-----|---------|-------|
5
5
  | [saml Architecture](architecture.md) | The `saml` repo is the SAML 2.0 / SSO gateway for all TOGa applications. | saml/index.php, saml/_.php, saml/Controller/Index.php, saml/Config/production.ini, saml/.platform/hooks/prebuild/git.sh, saml/.ebextensions/git.php |
6
+ | [SAML Downstream Integration Contract](features/downstream-integration-contract.md) | The contract a **downstream TOGa app** implements to authenticate users through the SAML gateway (`saml.togahub.com`). | saml/Controller/Index.php, _underscore/Model/Core/ClientAuthentication.php, _underscore/Model/True/ClientAuthentication.php |
6
7
  | [Rate SAML User Provisioning](features/rate-user-provisioning.md) | When a Rate user authenticates via SSO, `_Model_Rate_ClientAuthentication::getAuthenticatedSsoUser()` is called by the saml gateway. | _underscore/Model/Rate/ClientAuthentication.php, _underscore/Model/Rate/User.php, _underscore/Model/Rate/Customer.php, _underscore/Model/Rate/Contact.php |
@@ -7,7 +7,7 @@ client: shared
7
7
  type: architecture
8
8
  status: active
9
9
  updated: 2026-06-11
10
- owners: ["rgirish"]
10
+ owners: ["rgirish", "jcardinal"]
11
11
  files:
12
12
  - saml/index.php
13
13
  - saml/_.php
@@ -80,9 +80,11 @@ To onboard a new SSO client, add the class in `_underscore` — not in this repo
80
80
  ## Gotchas / known issues
81
81
 
82
82
  - **`_Database::register()` auto-starts a lazy transaction (since Apr 2 2026).** Any code that registers DBs then writes must call `_Database::transactionCommit()` before exiting — otherwise MySQL silently rolls back all writes on connection close. The `/acs` handler calls `transactionCommit()` after `getAuthenticatedSsoUser()` succeeds and before the redirect. See `_underscore/Database.php:48`.
83
- - **SAML signature verification is commented out.** The IdP X509 cert verification block is disabled. The only gate is the encrypted RelayState. Re-enabling per-IdP signature verification is a priority security improvement.
83
+ - **SAML signature verification is commented out — this is the repo's top security priority.** The IdP X509 cert verification block in the `/acs` flow is disabled, so the gateway accepts any well-formed, status-success `SAMLResponse` without proving it was signed by the expected IdP. The only remaining gate is the encrypted RelayState, which authenticates the *originating TOGa request* — not the *asserting IdP*. Threat model: an attacker who can craft or replay a `SAMLResponse` (or a malicious/compromised IdP) can forge an identity assertion for any user, since nothing binds the assertion to a trusted signing key. Re-enabling per-IdP X509 signature verification is the single highest-impact security fix for this repo.
84
+ - **Plaintext secrets in source — move to AWS SSM Parameter Store.** `saml/Config/production.ini` stores credentials in plaintext, and a Sentry DSN is hardcoded in `saml/Controller/Index.php`. Both are checked into the repo. Migrate these to AWS SSM Parameter Store (SecureString) and load at runtime; the source tree should reference parameter names only, never literal credential values.
84
85
  - **SLS not implemented.** `/sls` falls through to the default banner.
85
86
  - **`set_exception_handler(null)` at top of `saml()`.** Unhandled exceptions output raw PHP errors. All exceptions from `getAuthenticatedSsoUser()` are caught and sent to Sentry.
86
87
 
87
88
  ## Change history
88
89
  - 2026-06-11 — Added `transactionCommit()` before redirect; wrapped `getAuthenticatedSsoUser()` in try/catch with Sentry; echo+exit on auth failure (rgirish)
90
+ - 2026-06-25 — Sharpened signature-verification gotcha with explicit threat model and flagged it as the repo's top security priority; documented plaintext secrets in `production.ini` + hardcoded Sentry DSN in `Controller/Index.php` with SSM Parameter Store recommendation (jcardinal)
@@ -0,0 +1,81 @@
1
+ ---
2
+ title: SAML Downstream Integration Contract
3
+ framework: "2.0"
4
+ repo: saml
5
+ project: SAML SSO Gateway
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-06-25
10
+ owners: [jcardinal]
11
+ files:
12
+ - saml/Controller/Index.php
13
+ - _underscore/Model/Core/ClientAuthentication.php
14
+ - _underscore/Model/True/ClientAuthentication.php
15
+ related:
16
+ - ../architecture.md
17
+ - ../../_underscore/architecture.md
18
+ - ../../../clients/rate/features/saml-sso.md
19
+ ---
20
+
21
+ ## Summary
22
+
23
+ The contract a **downstream TOGa app** implements to authenticate users through the SAML gateway
24
+ (`saml.togahub.com`). The architecture doc covers the gateway internals; this doc is the
25
+ **integration recipe** for an app that wants SSO — how to initiate the flow, what the gateway
26
+ hands back, and how to consume it. Reusable for any new SSO-consuming app.
27
+
28
+ ## How it works
29
+
30
+ ### SP-initiated flow
31
+ 1. The downstream app calls
32
+ `_Model_Core_ClientAuthentication::singleSignOnServiceUrl(uuid, domainUuid, urlParameters)`
33
+ to build a signed SAML AuthnRequest (HTTP-Redirect binding). The RelayState
34
+ (`{v:1, time, domain, urlParameters}`) is encrypted with the Core `API_SECRET_ACCESS_TOKEN`
35
+ and carries a 300-second TTL.
36
+ 2. The user authenticates at the client IdP, which POSTs the `SAMLResponse` to the gateway `/acs`.
37
+ 3. `/acs` decrypts RelayState (supports key rotation via `API_SECRET_ACCESS_TOKEN_PREVIOUS`),
38
+ resolves Core `Domain → Client → Environment`, **dynamically registers the client DBs**
39
+ (`DB_CLIENT`, `DB_CLIENT_LOGS`, `DB_CLIENT_ARCHIVE`), then calls the client-specific mapper
40
+ `_Model_<ClientIdentifier>_ClientAuthentication::getAuthenticatedSsoUser($assertion)` to
41
+ resolve or create the user.
42
+ - Base `_Model_True_ClientAuthentication` matches by **email** (from the NameID).
43
+ - Compass matches `c_hrEmpUsername`; Rate matches `borrowerID`.
44
+
45
+ ### Handoff back to the downstream app
46
+ The gateway redirects the browser to:
47
+ ```
48
+ <appDomain>?saml=<base64(json)>
49
+ ```
50
+ where the JSON is:
51
+ ```json
52
+ {
53
+ "auth": "encrypted-user-uuid",
54
+ "payload": { "client": "encryptWithKey(client.uuid)", "user": "encryptWithKey(user.uuid)" }
55
+ }
56
+ ```
57
+ all encrypted under the Core `API_SECRET_ACCESS_TOKEN`. An optional `?urlParameters` is forwarded
58
+ through.
59
+
60
+ The downstream app then: base64-decodes the `saml` param, JSON-parses it, decrypts the client and
61
+ user UUIDs with `_String::decryptWithKey()`, loads the user, and **establishes its own session**.
62
+ The gateway holds no session — session ownership is entirely the downstream app's.
63
+
64
+ ### Integrating a NEW downstream app
65
+ 1. Ensure an SSO config row exists in `_Model_Core_ClientAuthentications_Sso`.
66
+ 2. Implement/extend `_Model_<Client>_ClientAuthentication::getAuthenticatedSsoUser()` in
67
+ `_underscore` (the gateway repo is not modified — see the per-client extension pattern in the
68
+ architecture doc).
69
+ 3. Initiate SSO via `singleSignOnServiceUrl()`.
70
+ 4. Consume the `?saml=` handoff and decrypt the UUIDs with `API_SECRET_ACCESS_TOKEN`.
71
+
72
+ ## Gotchas / known issues
73
+
74
+ - **Lazy-transaction write-drop.** `_Database::register()` auto-starts a lazy transaction (since
75
+ Apr 2026). Any `/acs` write path (e.g. user auto-creation) must call
76
+ `_Database::transactionCommit()` before exit or the writes are silently rolled back.
77
+ - **Gateway holds no session** — every downstream app must build and own its own session from the
78
+ decrypted handoff.
79
+
80
+ ## Change history
81
+ - 2026-06-25 — Documented the downstream integration contract (SP-initiated flow, `?saml=` handoff shape, and the 4 onboarding steps), discovered while planning a new SSO-consuming app (reference: saml) (jcardinal)
@@ -4,7 +4,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
4
4
 
5
5
  ## 1.0 framework
6
6
 
7
- - **library** (Library) _(framework core)_ — 8 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
7
+ - **library** (Library) _(framework core)_ — 9 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
8
8
  - **worker** (Worker) — 10 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
9
9
  - **togadesk** (TOGa Desk) — 8 doc(s) → [1.0/apps/togadesk/INDEX.md](1.0/apps/togadesk/INDEX.md)
10
10
  - **togaview** (TOGa View) — 6 doc(s) → [1.0/apps/togaview/INDEX.md](1.0/apps/togaview/INDEX.md)
@@ -20,7 +20,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
20
20
  - **api2** (API) — 6 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
21
21
  - **dbchanges2** (Database Changes) _(framework core)_ — 2 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
22
22
  - **toga2-supply** (TOGa Supply) — 3 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
23
- - **saml** (SAML SSO Gateway) — 2 doc(s) → [2.0/apps/saml/INDEX.md](2.0/apps/saml/INDEX.md)
23
+ - **saml** (SAML SSO Gateway) — 3 doc(s) → [2.0/apps/saml/INDEX.md](2.0/apps/saml/INDEX.md)
24
24
  - **toga2-view** (TOGa View Frontend) — 4 doc(s) → [2.0/apps/toga2-view/INDEX.md](2.0/apps/toga2-view/INDEX.md)
25
25
  - **toga2-hub** (TOGa Hub) — 2 doc(s) → [2.0/apps/toga2-hub/INDEX.md](2.0/apps/toga2-hub/INDEX.md)
26
26
  - **talos** (TOGa IQ) — 6 doc(s) → [2.0/apps/talos/INDEX.md](2.0/apps/talos/INDEX.md)
@@ -51,6 +51,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
51
51
  - **Quad Graphics** (`quad`) → [clients/quad/INDEX.md](clients/quad/INDEX.md)
52
52
  - **Rate** (`rate`) → [clients/rate/INDEX.md](clients/rate/INDEX.md)
53
53
  - **Tow Foundation** (`tow-foundation`) → [clients/tow-foundation/INDEX.md](clients/tow-foundation/INDEX.md)
54
+ - **TOGA Technology** (`true`) → [clients/true/INDEX.md](clients/true/INDEX.md)
54
55
  - **Walmart Client Profile** (`walmart`) → [clients/walmart/INDEX.md](clients/walmart/INDEX.md)
55
56
  - **Wiss, Janney, Elstner Associates, Inc.** (`wje`) → [clients/wje/INDEX.md](clients/wje/INDEX.md)
56
57
 
@@ -0,0 +1,6 @@
1
+ # Client: TOGA Technology `true`
2
+
3
+ | Doc | Framework | Summary | Files |
4
+ |-----|-----------|---------|-------|
5
+ | [True Users / Personas Data Model](features/users-personas-data-model.md) | 2.0 | The `Client_True` schema models internal/staff users and their **personas** — used to gate UI and navigation by staff role in downstream tooling. | |
6
+ | [TOGA Technology](profile.md) | 2.0 | "True" is TOGa's own **internal / staff tenant** — the base client whose data lives in the `Client_True` schema in production. | |
@@ -0,0 +1,45 @@
1
+ ---
2
+ title: True Users / Personas Data Model
3
+ framework: "2.0"
4
+ project: _Underscore
5
+ client: true
6
+ type: client-feature
7
+ status: active
8
+ updated: 2026-06-25
9
+ owners: [jcardinal]
10
+ files: []
11
+ related:
12
+ - clients/true/profile.md
13
+ - 2.0/apps/saml/features/downstream-integration-contract.md
14
+ ---
15
+
16
+ ## Summary
17
+
18
+ The `Client_True` schema models internal/staff users and their **personas** — used to gate UI and
19
+ navigation by staff role in downstream tooling. After SSO resolves a user, an app looks up that
20
+ user's personas and drives visibility from persona membership.
21
+
22
+ ## How it works
23
+
24
+ ### Tables
25
+ - **`Users`** — key columns: `id`, `uuid` (char, unique), `email` (varchar, unique), `firstName`,
26
+ `lastName`, `displayName`, `isActive` (tinyint, default `1`), `password` (char, nullable), plus
27
+ FKs `contactId`, `clientId`, `locationId`, `userTypeId`, `supervisorUserId`.
28
+ - **`Personas`** — `id`, `uuid`, `name`, `number`.
29
+ - **`Users_Personas`** (many-to-many bridge) — `id`, `uuid`, `userId`, `personaId`.
30
+
31
+ ### Live personas (production)
32
+ TOGa Technology, Development Team, Legal Team, Contact Center, Operations, Executive, Audit.
33
+
34
+ ### Usage pattern for downstream apps
35
+ After SSO resolves the user, look up their personas via
36
+ `Users JOIN Users_Personas JOIN Personas`, then drive UI / navigation visibility by persona
37
+ membership.
38
+
39
+ ## Gotchas / known issues
40
+
41
+ - **Cache personas in session.** Cache the resolved persona names in `$_SESSION` at login to avoid
42
+ a per-request DB read for visibility checks.
43
+
44
+ ## Change history
45
+ - 2026-06-25 — Documented the Client_True Users/Personas data model and persona-gated UI usage pattern, discovered via prod DB schema inspection while planning persona-gated internal tooling (jcardinal)
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: "TOGA Technology"
3
+ framework: "2.0"
4
+ apps:
5
+ - _underscore
6
+ project: _Underscore
7
+ client: true
8
+ type: profile
9
+ status: active
10
+ updated: 2026-06-25
11
+ owners: [jcardinal]
12
+ files: []
13
+ related:
14
+ - clients/true/features/users-personas-data-model.md
15
+ ---
16
+
17
+ ## Summary
18
+
19
+ "True" is TOGa's own **internal / staff tenant** — the base client whose data lives in the
20
+ `Client_True` schema in production. It is also the default SSO identity mapper
21
+ (`_Model_True_ClientAuthentication`, which matches users by email). Internal tooling that gates UI
22
+ by staff role (e.g. the planned Toolbox app) reads its `Users` / `Personas` model.
23
+
24
+ - **Client DB:** `Client_True`
25
+ - **Client identifier:** `True`
26
+ - **SSO mapper:** `_Model_True_ClientAuthentication` (base; matches by email from NameID)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.201",
3
+ "version": "1.0.202",
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",