toga-ai 1.0.549 → 1.0.550

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.
@@ -17,6 +17,7 @@
17
17
  | [ClickUp Work Type Automation (Committed / Conditional / Stretch)](features/clickup-work-type-automation.md) | The ClickUp webhook handler (`_Worker_Clickup`) automatically maintains each task's **Work Type** custom field — `Committed`, `Conditional`, or `Stretch` — base | worker2/Worker/Clickup.php, worker2/Tests/Worker/ClickupWorkTypeTest.php |
18
18
  | [Compass VIP Support Importer (worker2)](features/compass-vip-support-importer.md) | A worker2 action that ingests Compass's quarterly VIP spreadsheet and assigns each VIP user's support technician by setting `Users.c_supportedByUserId` in `Clie | worker2/Worker/Client/Compass/VipSupport.php |
19
19
  | [Creating Worker Actions](features/creating-worker-actions.md) | How to add a new callable Worker action — a PHP class whose `public static` methods are invoked as background jobs (via webhook, cron, or `_Worker::runTask()`). | worker2/Worker/, worker2/Controller/Index.php, _underscore/Worker.php |
20
+ | [Cross-account AWS access for worker2 crons (_Component_Aws_Workloads)](features/cross-account-aws-access.md) | `_Component_Aws_Workloads` is the **standard, and only sanctioned, way any new worker2 cron obtains AWS access** — for any account, any region, any AWS SDK clie | worker2/Component/Aws/Workloads/Workloads.php, worker2/Config/production.ini, worker2/Worker/Infrastructure/CloudWatch.php |
20
21
  | [Elite Freshservice Sync (worker2)](features/elite-freshservice-sync.md) | `_Worker_Elite` processes Freshservice webhook events and syncs them into TOGA 2. | worker2/Worker/Elite.php, worker2/Config/dev-kmaramreddy-laptop.ini |
21
22
  | [Error Escalation Cron (Errors::Escalate → ClickUp / email)](features/error-escalation-cron.md) | `_Worker_Infrastructure_Errors::Escalate` (renamed from `SyncWithClickup`) is the sole owner of **escalation, de-escalation, ClickUp ticketing, reminders, busin | worker2/Worker/Infrastructure/Errors.php, worker2/Worker/Notification/Email.php, worker2/Worker/Notification/EmailTemplate.php, worker2/Worker/Client/True.php, worker2/Worker/Clickup/ErrorTask.php, worker2/Worker/Clickup.php, worker2/Controller/Index.php, worker2/Config/production.ini, _underscore/Model/Core/Logs/Issue.php, dbchanges2/Core/2026-07-30a - Error escalation cron job.sql, dbchanges2/Logs/2026-08-03a - Issue clickupPriority.sql, dbchanges2/Core/2026-08-04a - Error neglect digest cron job.sql |
22
23
  | [Error-Issue Auto-Resolution & Reopen (frequency-decay lifecycle)](features/error-issue-auto-resolution.md) | The error system could escalate and de-escalate an Issue's *urgency* but had no concept of an Issue being **resolved**. | worker2/Worker/Infrastructure/Errors.php, worker2/Worker/Clickup/ErrorTask.php, _underscore/Model/Core/Logs/Issue.php, tools/mvc/errors/get.php, tools/mvc/errors/issue/get.php, dbchanges2/Logs/2026-08-05a - Issue status baseline and auto-resolution.sql |
@@ -0,0 +1,135 @@
1
+ ---
2
+ title: Cross-account AWS access for worker2 crons (_Component_Aws_Workloads)
3
+ framework: "2.0"
4
+ repo: worker2
5
+ project: Worker
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-08-10
10
+ owners: [jcardinal]
11
+ files:
12
+ - worker2/Component/Aws/Workloads/Workloads.php
13
+ - worker2/Config/production.ini
14
+ - worker2/Worker/Infrastructure/CloudWatch.php
15
+ related:
16
+ - ../../_underscore/features/component-model-namespace-registration.md
17
+ - ../../_underscore/features/config-group-access.md
18
+ - ../../_underscore/features/cloud-s3-helpers.md
19
+ - ./oneuptime-worker2-monitoring.md
20
+ ---
21
+
22
+ ## Summary
23
+
24
+ `_Component_Aws_Workloads` is the **standard, and only sanctioned, way any new worker2 cron
25
+ obtains AWS access** — for any account, any region, any AWS SDK client. It replaces
26
+ hand-rolled `StsClient`/credential code and, more importantly, replaces the **legacy static
27
+ IAM-user access key** with a role-based model: the Elastic Beanstalk EC2 **instance role is
28
+ the worker's identity**, and it **assumes a per-account `WorkloadsRuntime` role** via STS to
29
+ reach each target account.
30
+
31
+ **Standard for all new AWS-touching worker2 code:** obtain credentials/clients through
32
+ `_Component_Aws_Workloads`. Do **not** instantiate `StsClient` by hand, and do **not** use
33
+ the legacy `[cloud]` static access key in new code paths (see
34
+ [Legacy static key](#legacy-static-key--being-retired)).
35
+
36
+ ## Key files / entry points
37
+
38
+ - `worker2/Component/Aws/Workloads/Workloads.php` — `class _Component_Aws_Workloads`
39
+ (namespace `worker`, `extends \_Component`). Two static methods:
40
+ - `assumeCredentials(string $awsAccountId): array` → STS temporary credentials
41
+ `['key', 'secret', 'token']` for the `WorkloadsRuntime` role in the target account.
42
+ - `client(string $clientClass, string $awsAccountId, string $region): object` → a
43
+ ready-to-use SDK client of any `Aws\...\Client` class, already credentialed and
44
+ region-set.
45
+ - `worker2/Config/production.ini` — `[cloud]` group keys `workloads_role_name` and
46
+ `workloads_role_session`.
47
+ - `worker2/Worker/Infrastructure/CloudWatch.php` — first consumer (`ElasticBeanstalkHealth()`).
48
+
49
+ ## How it works
50
+
51
+ 1. The component builds an `StsClient` with **no explicit credentials**. The AWS SDK's
52
+ default provider chain therefore resolves to the **EB EC2 instance role**
53
+ (`ElasticBeanstalk-EC2-Instance-Profile`) — the worker's own identity. There are no
54
+ static keys in this path.
55
+ 2. That instance role holds an inline policy `AssumeWorkloadsRuntime`
56
+ (`sts:AssumeRole` on `arn:aws:iam::*:role/WorkloadsRuntime`), so it can assume the
57
+ `WorkloadsRuntime` role in **any** account.
58
+ 3. `assumeCredentials()` calls `AssumeRole` on
59
+ `arn:aws:iam::<awsAccountId>:role/WorkloadsRuntime` and returns the temporary creds.
60
+ 4. `client()` uses those creds to construct the requested SDK client for the given region.
61
+
62
+ Role name and session are read from the `[cloud]` config group via `_Config::cloud()` with
63
+ `isGroupSet` / `isGroupPropertySet` guards, defaulting **in code** to role
64
+ `WorkloadsRuntime` / session `worker2-workloads` when the keys are absent — so non-prod
65
+ `.ini` files that lack the keys keep working (see
66
+ [_Config group access](../../_underscore/features/config-group-access.md)).
67
+
68
+ This is another concrete instance of the app-repo namespace pattern: `namespace worker` +
69
+ `extends \_Component`, referenced unqualified from `_Worker_*` classes via the autoloader
70
+ alias (see
71
+ [Component/Model namespace registration](../../_underscore/features/component-model-namespace-registration.md)).
72
+
73
+ ## The WorkloadsRuntime role model (three accounts)
74
+
75
+ IAM is **global**, so **one `WorkloadsRuntime` role per account** covers all regions —
76
+ region is only a runtime argument to `client()`, never a reason for a second role.
77
+
78
+ - **654654170868** — production hub. Home region `us-east-1`; also used in `us-west-2` and
79
+ `eu-west-1`. worker2 runs here on Elastic Beanstalk; its instance profile role
80
+ `ElasticBeanstalk-EC2-Instance-Profile` is the caller identity.
81
+ - **502614707982** — legacy account, `us-west-2`.
82
+ - **975050298201** — non-production account, `us-east-1`.
83
+
84
+ Each `WorkloadsRuntime` role has broad service permissions (`elasticbeanstalk:*`, `ec2:*`,
85
+ `s3:*`, `sqs:*`, `cloudwatch:*`, `logs:*`, `ssm:*`, `autoscaling:*`,
86
+ `elasticloadbalancing:*`) and a **trust policy allowing the 868 instance role to assume
87
+ it**.
88
+
89
+ ## First consumer — ElasticBeanstalkHealth (worked example)
90
+
91
+ `_Worker_Infrastructure_CloudWatch::ElasticBeanstalkHealth(awsAccountId,
92
+ elasticBeanstalkEnvironmentNames, oneuptimeUrl, region)` is a **parameterized cron** (args
93
+ spread from `Core.CronJobs.parameters` as named args) that reads each EB environment's
94
+ overall health through `_Component_Aws_Workloads::client(...)`, maps `HealthStatus`
95
+ `Degraded`/`Severe` → `alarm:"HIGH"`, and pushes a **non-fatal** OneUptime heartbeat
96
+ (`setLogging(false)`, `setThrowExceptionsOnFailure(false)` — the Compass push pattern; see
97
+ [OneUptime worker2 monitoring](./oneuptime-worker2-monitoring.md)).
98
+
99
+ It was refactored this session to use the new helper: the prior inline STS/credential code
100
+ and the `HEALTH_ASSUME_ROLE_NAME` / `HEALTH_ASSUME_ROLE_SESSION` constants were removed in
101
+ favor of `_Component_Aws_Workloads` + the `[cloud]` config keys. The prior
102
+ `CheckBeanstalks()` action was renamed `ElasticBeanstalkInstances()`
103
+ (dbchanges2 `Core/2026-08-10c` updates the CronJobs action name; `Core/2026-08-10d` seeds
104
+ the new parameterized `ElasticBeanstalkHealth` cron row, `isActive=0`).
105
+
106
+ ## Legacy static key — being retired
107
+
108
+ The legacy `[cloud]` static access key is a **personal IAM user in account 502614707982**.
109
+ It is being retired for new code but is **still used by older code paths** — ZoomInfo,
110
+ VipSupport, and the `_Cloud` CLI path. **Retrofit of that existing code is deliberately
111
+ deferred** (developer decision, this session): document the new approach; do not migrate old
112
+ code yet. New code must not adopt the static key.
113
+
114
+ **Never record the access-key values in knowledge.** They exist in `production.ini`; that is
115
+ their only home.
116
+
117
+ ## Gotchas / known issues
118
+
119
+ - Do not construct `StsClient` or pass explicit credentials by hand in new crons — the whole
120
+ point is that the instance role is the caller and the component owns the assume-role step.
121
+ - The instance-role-as-caller only works **on the EB tier**. Off-tier (e.g. a local CLI run)
122
+ the SDK default chain resolves differently; that is why the legacy `_Cloud` CLI path still
123
+ uses the static key for now.
124
+ - Config keys are optional by design (in-code defaults), so a missing `[cloud]`
125
+ `workloads_role_*` key silently uses `WorkloadsRuntime` / `worker2-workloads` — set them
126
+ explicitly in prod ini to override.
127
+
128
+ ## Change history
129
+ - 2026-08-10 — Created. Added `_Component_Aws_Workloads` as the standard cross-account AWS
130
+ access helper (instance-role → per-account `WorkloadsRuntime` STS assume-role), documented
131
+ the three-account role model and `[cloud]` config keys, and recorded that the legacy static
132
+ IAM-user access key is retired for new code (existing ZoomInfo/VipSupport/_Cloud CLI paths
133
+ deferred). `ElasticBeanstalkHealth` is the first consumer. (jcardinal)
134
+ </content>
135
+ </invoke>
@@ -5,7 +5,7 @@ project: _Underscore
5
5
  client: shared
6
6
  type: standard
7
7
  status: active
8
- updated: 2026-08-03
8
+ updated: 2026-08-10
9
9
  owners: [jcardinal, mhammontree, dfranks]
10
10
  files: []
11
11
  related:
@@ -812,6 +812,25 @@ client sending a shallow depth.
812
812
 
813
813
  * Ensure code is well-documented with comments and usage examples.
814
814
 
815
+ ## AWS access (worker2)
816
+
817
+ ### Obtain AWS credentials via _Component_Aws_Workloads — never static keys in new code
818
+
819
+ New worker2 code that touches AWS (any account, any region, any SDK client) must obtain
820
+ credentials/clients through `_Component_Aws_Workloads::assumeCredentials()` /
821
+ `::client()`. Do **not** construct `StsClient` or pass explicit credentials by hand, and do
822
+ **not** use the legacy `[cloud]` static IAM-user access key in new code paths.
823
+
824
+ The model: the Elastic Beanstalk EC2 instance role (`ElasticBeanstalk-EC2-Instance-Profile`,
825
+ account 654654170868) is the worker's identity and assumes a per-account `WorkloadsRuntime`
826
+ role via STS. IAM is global, so one `WorkloadsRuntime` role per account covers all regions.
827
+
828
+ Existing static-key paths (ZoomInfo, VipSupport, `_Cloud` CLI) are grandfathered — retrofit
829
+ is deferred, not sanctioned for new work. Never record access-key values anywhere but the ini.
830
+
831
+ See: 2.0/apps/worker2/features/cross-account-aws-access.md
832
+
815
833
  ## Change history
816
834
 
835
+ - 2026-08-10 — Added "AWS access (worker2)" section: new worker2 AWS code must use `_Component_Aws_Workloads` (instance-role → per-account `WorkloadsRuntime` STS assume-role); legacy static key grandfathered for existing paths only. (jcardinal)
817
836
  - 2026-08-01 — Recorded the Logs-cluster singular table-naming exception to the otherwise-plural table convention. (jcardinal)
@@ -19,7 +19,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
19
19
  ## 2.0 framework
20
20
 
21
21
  - **_underscore** (_Underscore) _(framework core)_ — 50 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
22
- - **worker2** (Worker) — 45 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
22
+ - **worker2** (Worker) — 46 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
23
23
  - **api2** (API) — 22 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
24
24
  - **dbchanges2** (Database Changes) _(framework core)_ — 8 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
25
25
  - **toga2-supply** (TOGa Supply) — 5 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.549",
3
+ "version": "1.0.550",
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",