toga-ai 1.0.236 → 1.0.238
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,6 @@
|
|
|
6
6
|
| [Tools — Developers Folder (UUID & Password Generators)](features/developer-tools.md) | The first two tools shipped in the Tools app, both under the **Developers** folder and gated to personas **Development Team** / **TOGa Technology**. | tools/mvc/developers/uuid/get.php, tools/mvc/developers/password/get.php |
|
|
7
7
|
| [Tools MVC — Routing, CSRF & App_Database Access Patterns](features/mvc-data-access-patterns.md) | The load-bearing 1.0 (`App_`) framework conventions a developer needs when adding a page to the Tools app — URL routing, CSRF, and DB access through `App_Databa | tools/_/app/nav.php, tools/mvc/get.php |
|
|
8
8
|
| [Tools Persona-Gated Navigation (App_Nav)](features/persona-gated-navigation.md) | `App_Nav` is the Tools app's two-level, **persona-gated** navigation. | tools/_/app/nav.php, tools/mvc/get.php |
|
|
9
|
-
| [Tools SAML SSO Consumer & Persona-Gated Auth (App_Auth)](features/saml-sso-auth.md) | `App_Auth` is the Tools app's authentication layer: it consumes the SAML gateway `?saml=` handoff (see the 2.0 SAML downstream integration contract), establishe | tools/_/app/auth.php, tools/mvc/sso/initiate/get.php, tools/mvc/sso/get.php, tools/mvc/login/get.php, tools/mvc/login/post.php, tools/mvc/logout/get.php, tools/mvc/get.php, tools/config.production.ini |
|
|
9
|
+
| [Tools SAML SSO Consumer & Persona-Gated Auth (App_Auth)](features/saml-sso-auth.md) | `App_Auth` is the Tools app's authentication layer: it consumes the SAML gateway `?saml=` handoff (see the 2.0 SAML downstream integration contract), establishe | tools/_/app/auth.php, tools/mvc/sso/initiate/get.php, tools/mvc/sso/get.php, tools/mvc/login/get.php, tools/mvc/login/post.php, tools/mvc/logout/get.php, tools/mvc/get.php, tools/config.production.ini, tools/config.local.ini |
|
|
10
10
|
| [Talos Pricing UI (Onboarding, Dashboard, Benchmarks, Cost Factors + Estimator)](features/talos-pricing-ui.md) | The 1.0 (tools app) face of the **Talos Pricing Platform** — a "Talos Pricing" nav folder with four pages plus a client-side estimate engine. | tools/_/app/nav.php, tools/_/app/talos/estimator.php, tools/mvc/talos/onboarding/get.php, tools/mvc/talos/onboarding/post.php, tools/mvc/talos/pricing/get.php, tools/mvc/talos/benchmarks/get.php, tools/mvc/talos/factors/get.php, tools/mvc/talos/factors/post.php, tools/assets/css/style.css |
|
|
11
11
|
| [Deploying Tools to Elastic Beanstalk (PHP 8.5 / Amazon Linux 2023)](workflows/deploy-to-elastic-beanstalk-al2023.md) | How the **Tools** 1.0 app boots on Elastic Beanstalk running `PHP 8.5 on 64bit Amazon Linux 2023/4.13.1 (aarch64)`. | tools/.ebextensions/004_http_to_https.config, tools/.ebextensions/006_mount-s3fs.config, tools/.ebextensions/007_setup_export_cache_folders.config, tools/.ebextensions/008_setup_ldap.config, tools/.ebextensions/009_setup_phpini.config, tools/.ebextensions/020_setup_git_libraries.config, tools/.ebextensions/050_register_instance_to_shared_application_load_balancer.config, tools/ebs/git.json |
|
|
@@ -6,13 +6,14 @@ project: Tools
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-30
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- tools/_/app/nav.php
|
|
13
13
|
- tools/mvc/get.php
|
|
14
14
|
related:
|
|
15
15
|
- ./persona-gated-navigation.md
|
|
16
|
+
- ./saml-sso-auth.md
|
|
16
17
|
- ../architecture.md
|
|
17
18
|
- ../../library/features/mvc-page-pattern-and-app-skeleton.md
|
|
18
19
|
---
|
|
@@ -69,9 +70,26 @@ $res = App_Database::query($sql, 'db_team');
|
|
|
69
70
|
$rows = App_Database::buildArrayOfRows($res, false);
|
|
70
71
|
```
|
|
71
72
|
|
|
73
|
+
## Gotcha — a page CANNOT set HTTP status/headers (preloader already flushed)
|
|
74
|
+
`App_FrameworkIndex::render()` turns on the preloader and calls `App_Page::flushCapture()`
|
|
75
|
+
(`ob_end_flush + flush`, `library/app/page.php:213`) **before** `body() → App_MVC::loadFile()` runs
|
|
76
|
+
your mvc page. So by the time any page executes, **headers are already sent**. Calling
|
|
77
|
+
`http_response_code()`, `header()`, or `session_regenerate_id()` from inside a page **fatals**
|
|
78
|
+
("headers already sent"), and the error handler escalates that to an uncaught `ErrorException` → a
|
|
79
|
+
bare **500**. Always guard:
|
|
80
|
+
|
|
81
|
+
```php
|
|
82
|
+
if (!headers_sent()) { http_response_code(401); } // matches App_MVC::routeTo's own check
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`establishSession()` (auth.php) guards `session_regenerate_id` for the same reason; the SSO failure
|
|
86
|
+
path hit this too — see [saml-sso-auth](./saml-sso-auth.md). The clean long-term fix is to handle
|
|
87
|
+
status-setting routes **before any output** and `exit`.
|
|
88
|
+
|
|
72
89
|
## Autoloader
|
|
73
90
|
`App_Foo_Bar` → `app/foo/bar.php`, **all lowercase** (per the library CLAUDE.md). E.g.
|
|
74
91
|
`App_Talos_Estimator` → `_/app/talos/estimator.php`.
|
|
75
92
|
|
|
76
93
|
## Change history
|
|
94
|
+
- 2026-06-30 — Added the preloader-flush gotcha: pages run after `App_Page::flushCapture()` flushes the output buffer, so `http_response_code`/`header`/`session_regenerate_id` from inside a page fatal on headers-already-sent (escalates to a 500) — guard with `!headers_sent()`. Surfaced by the SSO failure-path 500 fix. (jcardinal)
|
|
77
95
|
- 2026-06-29 — Documented tools MVC routing (GET→get.php / POST→post.php; writes via mvc/<route>/post.php, not the legacy actionHandler path), Origin/Referer CSRF (no token field), `App_Database` query/row/txn API + `sqlEscape()` name, the by-reference row-helper warning gotcha, the persona-narrowing gotcha, and the lowercase autoloader mapping. Discovered building the Talos Pricing UI. (jcardinal)
|
|
@@ -6,7 +6,7 @@ project: Tools
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-30
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- tools/_/app/auth.php
|
|
@@ -17,6 +17,7 @@ files:
|
|
|
17
17
|
- tools/mvc/logout/get.php
|
|
18
18
|
- tools/mvc/get.php
|
|
19
19
|
- tools/config.production.ini
|
|
20
|
+
- tools/config.local.ini
|
|
20
21
|
related:
|
|
21
22
|
- ../architecture.md
|
|
22
23
|
- ../../library/features/app-sso-initiation.md
|
|
@@ -49,14 +50,30 @@ auto-created.
|
|
|
49
50
|
### Consuming the `?saml=` handoff (`mvc/sso/get.php` → `App_Auth`)
|
|
50
51
|
1. Length-guard `$_GET['saml']`, then base64-decode → `json_decode` → read `payload.client`
|
|
51
52
|
and `payload.user`.
|
|
52
|
-
2. Decrypt each with `App_String::decryptWithKey()`
|
|
53
|
-
with a **dual-key
|
|
54
|
-
rotation
|
|
55
|
-
|
|
53
|
+
2. Decrypt each with `App_String::decryptWithKey()` (AES-256-CBC; see crypto format below) via
|
|
54
|
+
`App_Auth::decryptHandoffValue()`, with a **current → previous dual-key fallback** so an
|
|
55
|
+
in-flight gateway key rotation still decodes. **Keys come from `Core.Parameters` at runtime,
|
|
56
|
+
not from config** — see "Handoff key sourcing" below. This interoperates byte-for-byte with
|
|
57
|
+
the 2.0 `_String::encryptWithKey()` that produced the token.
|
|
56
58
|
3. Validate the decrypted **client uuid == configured `true_client_uuid`** via `hash_equals`,
|
|
57
59
|
and that both decrypted values match a UUID regex.
|
|
58
60
|
4. Load the active Client_True user: `WHERE uuid = ? AND isActive = 1`. No match → fail closed.
|
|
59
61
|
|
|
62
|
+
### Handoff key sourcing (`handoffKeys()` → `Core.Parameters`)
|
|
63
|
+
The shared secret is **rotated regularly**, so the static `config.*.ini [saml]` token goes stale —
|
|
64
|
+
that staleness was exactly why `decryptHandoffValue()` returned `false` and production login failed
|
|
65
|
+
(the payload decoded to the correct iv+base64 shape; it was a **key** mismatch, not a format bug).
|
|
66
|
+
- `App_Auth::handoffKeys()` (new private helper) `SELECT \`key\`,\`value\` FROM Parameters WHERE
|
|
67
|
+
\`key\` IN ('API_SECRET_ACCESS_TOKEN','API_SECRET_ACCESS_TOKEN_PREVIOUS')` over the **`db_toga2core`**
|
|
68
|
+
connection (2.0 PROD Core DB; `const CORE_DB = 'db_toga2core'`). Result is **cached per request**.
|
|
69
|
+
`\`key\`` and `\`value\`` are SQL reserved words → **must be backticked**.
|
|
70
|
+
- `decryptHandoffValue()` then tries the current key, falling back to previous (rotation window).
|
|
71
|
+
Its **public signature is unchanged** — only `mvc/sso/get.php` calls it.
|
|
72
|
+
- The old `[saml] api_secret_access_token` / `_previous` config values are now **dead** (removable).
|
|
73
|
+
`[saml] true_client_uuid` is **still used** (`sso/get.php:80-84`) and stays.
|
|
74
|
+
- See [Core.Parameters key/value store](#coreparameters-key-value-store) for the table shape and
|
|
75
|
+
which DB cluster it lives on.
|
|
76
|
+
|
|
60
77
|
### Session establishment (`establishSession()`)
|
|
61
78
|
- Calls `session_regenerate_id(true)` **guarded by `if (!headers_sent())`** (see gotcha), then
|
|
62
79
|
caches the user and **persona names** in `$_SESSION`.
|
|
@@ -88,22 +105,51 @@ removed.
|
|
|
88
105
|
**inside** the page render (`frameworkindex → body → loadFile`) after `common/header.php` has
|
|
89
106
|
emitted output, so `session_regenerate_id(true)` fatals ("cannot be regenerated after headers
|
|
90
107
|
already sent"). Fixed by guarding with `if (!headers_sent())`. **Known architectural limitation:**
|
|
91
|
-
the
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
108
|
+
the proper long-term fix is to route `/sso` **before any output** and `exit`. Any 1.0 app adopting
|
|
109
|
+
this consumer pattern inherits this — see [App_Sso](../../library/features/app-sso-initiation.md)
|
|
110
|
+
and the [preloader-flush gotcha](./mvc-data-access-patterns.md) for the general rule.
|
|
111
|
+
- **Failure path also fataled → fixed (2026-06-30).** `tools_ssoFail()` called
|
|
112
|
+
`http_response_code(401)` to render the "Authentication failed" page, which itself fataled with
|
|
113
|
+
"headers already sent" and the error handler escalated it to an uncaught `ErrorException` → a bare
|
|
114
|
+
**500** instead of the intended 401 page. Root cause: `App_FrameworkIndex::render()` turns on the
|
|
115
|
+
preloader and runs `App_Page::flushCapture()` (`ob_end_flush + flush`, `library/app/page.php:213`)
|
|
116
|
+
**before** `body()/App_MVC::loadFile()` runs the mvc page. Fixed with the same
|
|
117
|
+
`if (!headers_sent()) { http_response_code(401); }` guard (matches `App_MVC::routeTo`'s own check).
|
|
95
118
|
- **No app-side replay defense.** The handoff token carries no nonce/timestamp the app verifies.
|
|
96
119
|
Recommend the gateway embed `iat` + `jti`.
|
|
97
120
|
|
|
121
|
+
## Crypto format (`App_String::encryptWithKey` / `decryptWithKey`)
|
|
122
|
+
`library/app/string.php:1075-1094`, AES-256-CBC: `encryptWithKey` returns
|
|
123
|
+
`base64( iv . openssl_encrypt(..., flag 0) )` — i.e. a base64 envelope whose body is the raw IV
|
|
124
|
+
followed by openssl's own base64 ciphertext; `decryptWithKey` reverses it. **Interoperable
|
|
125
|
+
byte-for-byte with the 2.0 `_String::decryptWithKey()`** that the SAML gateway uses to produce the
|
|
126
|
+
handoff token. The 2026-06-30 login failure decoded to the exact expected iv+base64 shape, proving
|
|
127
|
+
the issue was a stale **key**, not a cipher/format mismatch.
|
|
128
|
+
|
|
129
|
+
## Core.Parameters key/value store
|
|
130
|
+
The platform's rotating key/value parameter store, on the **2.0 PROD Core DB** (`Core` schema).
|
|
131
|
+
Columns: `id`, `uuid`, `\`key\`` (UNIQUE), `\`value\`` — `key`/`value` are reserved words and **must
|
|
132
|
+
be backticked**. Holds the SSO/API shared secrets `API_SECRET_ACCESS_TOKEN` and
|
|
133
|
+
`API_SECRET_ACCESS_TOKEN_PREVIOUS`. The tools app reaches it via the **`db_toga2core`** connection
|
|
134
|
+
(`[database_toga2core]`, dbname `Core`, prod core cluster in `us-west-2`) — a cluster **distinct**
|
|
135
|
+
from the 1.0 prod-cluster and from the 2.0 client cluster that backs `db_true`/`Client_True`.
|
|
136
|
+
|
|
98
137
|
## Config keys
|
|
99
138
|
|
|
100
|
-
`[database_true]` (read-only Client_True);
|
|
101
|
-
`
|
|
102
|
-
`
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
139
|
+
`[database_true]` (read-only Client_True); **`[database_toga2core]`** — new requirement: alias
|
|
140
|
+
`db_toga2core`, dbname `Core`, the prod core cluster host (`production-core-cluster…us-west-2…`),
|
|
141
|
+
needed in **each** `config.*.ini` so the handoff keys can be read at runtime (developer is adding
|
|
142
|
+
creds). `[saml]`: `true_client_uuid` (still used), `client_authentication_uuid`, `domain_uuid`,
|
|
143
|
+
plus the IdP/ACS urls used by initiation; `[internal]` `dev_mode`. The old `[saml]
|
|
144
|
+
api_secret_access_token` / `_previous` are now **dead** (superseded by `Core.Parameters`).
|
|
145
|
+
|
|
146
|
+
**Secret location.** Rotating secrets are now sourced from `Core.Parameters` **at runtime** rather
|
|
147
|
+
than static config — an improvement over the old committed `config.production.ini [saml]` tokens.
|
|
148
|
+
A **pre-existing reused plaintext DB password** across the `config.*.ini [database_*]` sections
|
|
149
|
+
remains (already noted in tools knowledge) — **flag for rotation.** Document **where** secrets live,
|
|
150
|
+
never the values.
|
|
106
151
|
|
|
107
152
|
## Change history
|
|
153
|
+
- 2026-06-30 — Fixed prod SSO login failure: handoff decrypt keys now sourced from `Core.Parameters` (rotated `API_SECRET_ACCESS_TOKEN`/`_PREVIOUS`) via new `App_Auth::handoffKeys()` over `db_toga2core` (per-request cached, current→previous fallback), instead of the stale committed `config [saml]` tokens — the stale key was the root cause. Added `[database_toga2core]` connection requirement to every config and documented the AES-256-CBC crypto format. Fixed `tools_ssoFail()` 500→intended 401: `http_response_code(401)` fataled on headers-already-sent (preloader flushes before mvc page runs); guarded with `!headers_sent()`. (jcardinal)
|
|
108
154
|
- 2026-06-26 — Wired up real SSO initiation via the new 1.0 `App_Sso` library class (`/sso/initiate`), replacing the `initiation_url` placeholder; registered the fixed-uuid Core.Domains return row (dbchanges2 `2026-06-26a`); fixed the `session_regenerate_id` headers-already-sent fatal (guarded with `!headers_sent()`) and documented the mid-render failure-path limitation; collapsed home → `/login` to one-step sign-in; noted prod secrets live in `config.production.ini [saml]` (jcardinal)
|
|
109
155
|
- 2026-06-25 — Built App_Auth: SAML `?saml=` handoff consumer with dual-key decrypt, hash_equals client-uuid check, fail-closed 401, persona-cached session (HttpOnly+SameSite=Lax), and a double-gated dev bypass. Initiation + replay defense left as open items (jcardinal)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: session
|
|
3
|
+
slug: talos-pricing-platform
|
|
4
|
+
title: Talos pricing platform (DB + tools UI + worker2 automation) and tools SSO login fix
|
|
5
|
+
author: jcardinal
|
|
6
|
+
repos: [tools, worker2, dbchanges2]
|
|
7
|
+
framework: "both"
|
|
8
|
+
client: shared
|
|
9
|
+
status: active
|
|
10
|
+
created: 2026-06-30
|
|
11
|
+
updated: 2026-06-30
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Session: talos-pricing-platform
|
|
15
|
+
**Date:** 2026-06-30
|
|
16
|
+
**Project/Repo:** tools (1.0) + worker2 (2.0) + dbchanges2 (2.0 Team DB)
|
|
17
|
+
**Task:** Build a data-driven Talos pricing platform (Team-DB schema + tools onboarding/dashboard UI + worker2 cron automation) to replace the Excel calculator; then fix tools SSO login that was failing in production.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## What WORKED
|
|
22
|
+
- **Schema (Team DB):** `dbchanges2/Team/2026-06-29a - TalosPricingTables.sql` — 9 tables created, SQL-reviewed (0 critical), all 7 important review fixes applied (dtUpdated on fact tables, DECIMAL(6,4) margins, isFeature* boolean naming, DECIMAL(14,8) calibration factors, full column comments, band-range CHECK). Tables live in the **Team** DB; `dbchanges2/Team/` is the migration folder (Team-DB migrations use unqualified table names).
|
|
23
|
+
- **worker2 automation:** `worker2/Worker/Talos/Pricing.php` (`_Worker_Talos_Pricing`) + `worker2/Database/TalosPricingCrons.sql` — `php -l` clean. Three monthly cron actions: ImportAwsActuals (Cost Explorer), RecomputeMargins (calibration + margin/streak/recommendation), MonthlyReport (PhpSpreadsheet xlsx + templated email). initialize() registers Team DB via `_underscore::DB_TEAM` (mirrors `_Worker_Team_Transcripts`).
|
|
24
|
+
- **tools UI:** nav folder + 6 pages + estimator + CSS, all `php -l` clean. `App_Talos_Estimator` (`tools/_/app/talos/estimator.php`), onboarding get/post, pricing dashboard, usage benchmarks, cost-factors get/post. Live JS estimate + recommended per-band fees on the onboarding form.
|
|
25
|
+
- **SSO 500 fix:** `tools/mvc/sso/get.php` `tools_ssoFail()` — added `if (!headers_sent())` guard around `http_response_code(401)`. `php -l` clean. Root cause confirmed: `App_FrameworkIndex::render()` flushes the preloader buffer (`page.php:213`) BEFORE `body()`/`loadFile()`, so headers are already sent when an mvc page runs.
|
|
26
|
+
- **SSO key sourcing:** `tools/_/app/auth.php` `App_Auth::decryptHandoffValue()` rewritten to read `API_SECRET_ACCESS_TOKEN` / `API_SECRET_ACCESS_TOKEN_PREVIOUS` from `Core.Parameters` (rotated there) via new cached `handoffKeys()` + `const CORE_DB='db_toga2core'`. `php -l` clean.
|
|
27
|
+
- **Crypto format confirmed:** `App_String::encryptWithKey/decryptWithKey` (library/app/string.php:1075-1094) = AES-256-CBC, `base64(iv . base64ciphertext)`, interoperable with 2.0 `_String`. The user's real `?saml=` payload decoded to exactly that shape — proving the SSO failure was a stale-key/config mismatch, not a format bug.
|
|
28
|
+
- **Knowledge captured + pushed twice:** pricing platform (6 docs incl. an approved ELEVATED architecture section in `2.0/apps/talos/architecture.md`) and SSO fixes (2 tools feature-doc updates). Both PUSHED to `_main`.
|
|
29
|
+
|
|
30
|
+
## What did NOT work — DO NOT RETRY THESE
|
|
31
|
+
- **`App_Database::escape()` does NOT exist** — the 1.0 escaper is `App_Database::sqlEscape()`. Using `::escape` fatals.
|
|
32
|
+
- **Passing `App_Database::query(...)` inline to `buildArrayOfRows()`/`fetchOne()`** triggers PHP "Only variables should be passed by reference" (both take `&$res`); with display_errors on it renders into output. MUST assign the query result to a variable first (the login post.php does this).
|
|
33
|
+
- **Reading SSO tokens from `config.production.ini` `[saml]`** — they go stale because the gateway rotates `API_SECRET_ACCESS_TOKEN`; that stale token was why decrypt returned false and login failed. Do not rely on config tokens; use Core.Parameters.
|
|
34
|
+
- **Calling `http_response_code()` / `header()` / `session_regenerate_id()` from any page loaded inside `body()`** — the preloader buffer is already flushed, so it fatals/no-ops. Always guard with `headers_sent()`.
|
|
35
|
+
- **Writing `[database_team]`/`[database_toga2core]` into config with a guessed host** — avoided deliberately; the 2.0 Core/Team host (`production-core-cluster…us-west-2`, from config.alpha.ini `[database_prod_toga2core]`) differs from the 1.0 prod-cluster and the 2.0 client cluster (`reader1.client.database.togahub.com` = db_true). Don't assume they're the same host.
|
|
36
|
+
|
|
37
|
+
## Not tried yet (candidates for next session)
|
|
38
|
+
- Seeding/calibrating `TalosCostFactors` from the real Langfuse aggregates (estimator currently ships placeholder DEFAULTS).
|
|
39
|
+
- Wiring the xlsx as an actual email attachment — `_Email` attachment support is unconfirmed (`MonthlyReport` writes xlsx to a temp path; email body has the HTML table meanwhile).
|
|
40
|
+
- Confirming the AWS Cost Explorer cost-allocation tag key (worker assumes `Client`).
|
|
41
|
+
- Creating the leadership `EmailTemplates` record in Client_True and replacing the placeholder `REPORT_EMAIL_TEMPLATE` UUID in `_Worker_Talos_Pricing`.
|
|
42
|
+
- Verifying the live SSO login end-to-end after the `[database_toga2core]` connection is added + deployed.
|
|
43
|
+
|
|
44
|
+
## Current file state
|
|
45
|
+
| File | Status | Notes |
|
|
46
|
+
|------|--------|-------|
|
|
47
|
+
| dbchanges2/Team/2026-06-29a - TalosPricingTables.sql | created | 9 Talos* tables, reviewed+fixed. Apply to Team DB. |
|
|
48
|
+
| worker2/Worker/Talos/Pricing.php | created | `_Worker_Talos_Pricing`; lint clean. REPORT_EMAIL_TEMPLATE is a placeholder UUID. |
|
|
49
|
+
| worker2/Database/TalosPricingCrons.sql | created | 3 Core.CronJobs seeds (4th of month). Not yet inserted. |
|
|
50
|
+
| tools/_/app/nav.php | modified | Added "Talos Pricing" folder (4 links; Cost Factors narrowed to technical personas). |
|
|
51
|
+
| tools/_/app/talos/estimator.php | created | `App_Talos_Estimator`; placeholder DEFAULT factors. |
|
|
52
|
+
| tools/mvc/talos/onboarding/get.php + post.php | created | Form + live estimate; writes TalosClients + TalosPricingBands in a txn. |
|
|
53
|
+
| tools/mvc/talos/pricing/get.php | created | Read-only margin/recommendation dashboard (db_team). |
|
|
54
|
+
| tools/mvc/talos/benchmarks/get.php | created | Per-user-per-month usage averages (db_team). |
|
|
55
|
+
| tools/mvc/talos/factors/get.php + post.php | created | Technical-only cost-factor editor (upsert TalosCostFactors). |
|
|
56
|
+
| tools/assets/css/style.css | modified | Appended .pricing-table / .badge / form-grid / estimate-readout styles. |
|
|
57
|
+
| tools/mvc/sso/get.php | modified | headers_sent() guard in tools_ssoFail() (500→graceful 401). |
|
|
58
|
+
| tools/_/app/auth.php | modified | decryptHandoffValue() reads keys from Core.Parameters via db_toga2core; added handoffKeys() + CORE_DB const. |
|
|
59
|
+
|
|
60
|
+
## Decisions made
|
|
61
|
+
- **Pricing model:** flat monthly ORG FEE (the adjustable margin lever) + per-user fee that STEPS by user-count band; band rate schedule locked at signing. On a band crossing the per-user fee auto-steps to the agreed rate, THEN the org fee is adjusted to restore margin. Rejected: pure per-user banded (no lever) and both-vary.
|
|
62
|
+
- **Cost methodology:** calibrate Langfuse→AWS (monthly factor = AWS_actual / Langfuse_costCorrected; new clients use a global blended factor). Rejected: AWS-only and Langfuse-only. Always use Langfuse's cache-corrected cost (it undercounts cache badly).
|
|
63
|
+
- **Table location:** the **Team** DB (`_underscore::DB_TEAM`) — what the user called "Core.Teams". Tenant link to Core.Clients is a plain indexed column, NOT a hard FK (Team DB may be a separate instance).
|
|
64
|
+
- **Onboarding writes:** direct DB write from the 1.0 tools app (not via api2) for speed.
|
|
65
|
+
- **SSO keys from Core.Parameters:** sourced at runtime (rotated) instead of static config, via a `db_toga2core` connection alias; keeps current→previous rotation fallback. Rejected: continuing to read config `[saml]` tokens.
|
|
66
|
+
|
|
67
|
+
## Blockers
|
|
68
|
+
- **SSO login still fails until the `[database_toga2core]` connection is added to the tools configs and tools is redeployed** — the `db_toga2core` alias is referenced by `App_Auth::CORE_DB` but no `[database_toga2core]` section exists in `config.production.ini` yet (developer is handling creds). Section template: hostname `production-core-cluster.cluster-clwbyqvxdm4q.us-west-2.rds.amazonaws.com`, username awsroot, dbname `Core`.
|
|
69
|
+
- All Talos UI pages need the `[database_team]` connection (db_team → Team DB) before they render data (also developer-supplied creds).
|
|
70
|
+
|
|
71
|
+
## Exact next step
|
|
72
|
+
> Add the `[database_toga2core]` section (alias db_toga2core, dbname=Core, host=production-core-cluster…us-west-2) to `tools/config.production.ini` (and the dev configs you test with), deploy tools, then attempt a fresh SSO login at https://tools.togatech.com/sso to confirm `decryptHandoffValue()` succeeds against the live rotated key.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
_Saved by /session-save on 2026-06-30_
|
package/package.json
CHANGED