Commit graph

24 commits

Author SHA1 Message Date
Adam Moussa
2d74f886bc
Document CDK app and cdk.json in README (#25)
Some checks failed
deploy / deploy (push) Has been cancelled
The README described the sh-mcp-auth stack's contents but never
documented that the repo root is a CDK app or what cdk.json is.
Add an Infrastructure (CDK app) section covering the cdk.json app
config, the infra/bin/app.ts entry point, the stacks, deploy-time
context inputs, and synth/deploy commands, and list cdk.json and
infra/ in the layout so the IaC component is discoverable.
2026-07-10 16:07:32 -04:00
Adam Moussa
6ce238a6a7
INFRA-136: add standard labeler caller (#24)
Some checks failed
deploy / deploy (push) Has been cancelled
* ci: add standard labeler caller (INFRA-136)

Adds the org standard callable-labeler thin caller, missing on this repo
(present on 26/28 repos). All three permission grants are load-bearing;
omitting one causes a silent startup_failure.

* Update labeler workflow to specific commit
2026-07-08 16:39:36 -04:00
dependabot[bot]
57d4f9a212
chore(deps-dev): bump typescript from 5.6.3 to 6.0.3 (#23)
Some checks failed
deploy / deploy (push) Has been cancelled
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.6.3 to 6.0.3.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.6.3...v6.0.3)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 6.0.3
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 23:54:04 +00:00
dependabot[bot]
5881770a90
chore(deps-dev): bump eslint from 9.39.4 to 10.6.0 (#22)
Bumps [eslint](https://github.com/eslint/eslint) from 9.39.4 to 10.6.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.4...v10.6.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.6.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 19:51:10 -04:00
dependabot[bot]
dbdea98f69
chore(deps-dev): bump vitest and @vitest/coverage-v8 from 3.x to 4.1.10 (#13)
* chore(deps-dev): bump vitest from 3.2.7 to 4.1.10

Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 3.2.7 to 4.1.10.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.10/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 4.1.10
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: bump @vitest/coverage-v8 alongside vitest to 4.1.10

The vitest 4.x bump had a peer dependency conflict with @vitest/coverage-v8
still pinned at 3.x. Bump it in lockstep to resolve npm install errors.

Refs #13

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: seahaven-openswe[bot] <296972425+seahaven-openswe[bot]@users.noreply.github.com>
2026-07-06 23:43:30 +00:00
seahaven-openswe[bot]
e330e240dd
fix(ci): group vitest and @vitest/* updates in dependabot (#21)
vitest and @vitest/coverage-v8 share a strict peer dependency. Bumping them
in separate Dependabot PRs breaks npm ci. Grouping them ensures they stay
in lockstep.

Co-authored-by: adam <166072409+amoussa1229@users.noreply.github.com>
2026-07-06 19:33:08 -04:00
dependabot[bot]
92ca5e3b6c
chore(deps-dev): bump eslint from 9.14.0 to 9.39.4 (#19)
Some checks are pending
deploy / deploy (push) Waiting to run
Bumps [eslint](https://github.com/eslint/eslint) from 9.14.0 to 9.39.4.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.14.0...v9.39.4)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.39.4
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 22:35:02 +00:00
Adam Moussa
b1e15f01a9
chore(ci): SHA-pin org reusable-workflow caller refs (INFRA-50) (#20) 2026-07-06 18:27:22 -04:00
dependabot[bot]
a2213ef315
chore(deps-dev): bump @types/node from 22.7.4 to 26.1.0 (#14)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.7.4 to 26.1.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.1.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 18:17:11 -04:00
dependabot[bot]
e992a288e6
chore(deps-dev): bump the minor-and-patch group across 1 directory with 10 updates (#12)
Some checks are pending
deploy / deploy (push) Waiting to run
Bumps the minor-and-patch group with 10 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.17.0` | `8.63.0` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.17.0` | `8.63.0` |
| [aws-cdk](https://github.com/aws/aws-cdk-cli/tree/HEAD/packages/aws-cdk) | `2.1128.1` | `2.1129.0` |
| [aws-cdk-lib](https://github.com/aws/aws-cdk/tree/HEAD/packages/aws-cdk-lib) | `2.260.0` | `2.261.0` |
| [prettier](https://github.com/prettier/prettier) | `3.4.2` | `3.9.4` |
| [tsx](https://github.com/privatenumber/tsx) | `4.22.4` | `4.23.0` |
| [@aws-sdk/client-cognito-identity-provider](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-cognito-identity-provider) | `3.1075.0` | `3.1080.0` |
| [@aws-sdk/client-dynamodb](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-dynamodb) | `3.1075.0` | `3.1080.0` |
| [@aws-sdk/client-secrets-manager](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-secrets-manager) | `3.1075.0` | `3.1080.0` |
| [@aws-sdk/lib-dynamodb](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/lib/lib-dynamodb) | `3.1075.0` | `3.1080.0` |



Updates `@typescript-eslint/eslint-plugin` from 8.17.0 to 8.63.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.63.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.17.0 to 8.63.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.63.0/packages/parser)

Updates `aws-cdk` from 2.1128.1 to 2.1129.0
- [Release notes](https://github.com/aws/aws-cdk-cli/releases)
- [Commits](https://github.com/aws/aws-cdk-cli/commits/aws-cdk@v2.1129.0/packages/aws-cdk)

Updates `aws-cdk-lib` from 2.260.0 to 2.261.0
- [Release notes](https://github.com/aws/aws-cdk/releases)
- [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md)
- [Commits](https://github.com/aws/aws-cdk/commits/v2.261.0/packages/aws-cdk-lib)

Updates `prettier` from 3.4.2 to 3.9.4
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.4.2...3.9.4)

Updates `tsx` from 4.22.4 to 4.23.0
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.22.4...v4.23.0)

Updates `@aws-sdk/client-cognito-identity-provider` from 3.1075.0 to 3.1080.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-cognito-identity-provider/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1080.0/clients/client-cognito-identity-provider)

Updates `@aws-sdk/client-dynamodb` from 3.1075.0 to 3.1080.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-dynamodb/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1080.0/clients/client-dynamodb)

Updates `@aws-sdk/client-secrets-manager` from 3.1075.0 to 3.1080.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-secrets-manager/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1080.0/clients/client-secrets-manager)

Updates `@aws-sdk/lib-dynamodb` from 3.1075.0 to 3.1080.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/lib/lib-dynamodb/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1080.0/lib/lib-dynamodb)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-cognito-identity-provider"
  dependency-version: 3.1080.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: "@aws-sdk/client-dynamodb"
  dependency-version: 3.1080.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: "@aws-sdk/client-secrets-manager"
  dependency-version: 3.1080.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: "@aws-sdk/lib-dynamodb"
  dependency-version: 3.1080.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.63.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.63.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: aws-cdk
  dependency-version: 2.1129.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: aws-cdk-lib
  dependency-version: 2.261.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: prettier
  dependency-version: 3.9.4
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: tsx
  dependency-version: 4.23.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 22:02:03 +00:00
dependabot[bot]
14cc4481ba
chore(deps-dev): bump typescript from 5.9.3 to 6.0.3 in /infra (#11)
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 6.0.3.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v6.0.3)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 6.0.3
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 21:56:32 +00:00
dependabot[bot]
266eded8c3
chore(deps-dev): bump @types/node from 22.20.0 to 26.1.0 in /infra (#10)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.20.0 to 26.1.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.1.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 21:53:38 +00:00
Adam Moussa
eb075ad3b5
docs: link Confluence AWS Architecture Map (INFRA-53) (#18) 2026-07-06 17:44:42 -04:00
Adam Moussa
cbc489514d
docs: add README status badges (INFRA-137) (#17) 2026-07-06 17:41:18 -04:00
Adam Moussa
3ba4696e0d
chore(ci): add org dependency-review caller (INFRA-125) (#16) 2026-07-06 17:40:50 -04:00
dependabot[bot]
7aa0d2f813
chore(deps-dev): bump the minor-and-patch group in /infra with 3 updates (#8)
Bumps the minor-and-patch group in /infra with 3 updates: [aws-cdk](https://github.com/aws/aws-cdk-cli/tree/HEAD/packages/aws-cdk), [aws-cdk-lib](https://github.com/aws/aws-cdk/tree/HEAD/packages/aws-cdk-lib) and [tsx](https://github.com/privatenumber/tsx).


Updates `aws-cdk` from 2.1128.1 to 2.1129.0
- [Release notes](https://github.com/aws/aws-cdk-cli/releases)
- [Commits](https://github.com/aws/aws-cdk-cli/commits/aws-cdk@v2.1129.0/packages/aws-cdk)

Updates `aws-cdk-lib` from 2.260.0 to 2.261.0
- [Release notes](https://github.com/aws/aws-cdk/releases)
- [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md)
- [Commits](https://github.com/aws/aws-cdk/commits/v2.261.0/packages/aws-cdk-lib)

Updates `tsx` from 4.22.4 to 4.23.0
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.22.4...v4.23.0)

---
updated-dependencies:
- dependency-name: aws-cdk
  dependency-version: 2.1129.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: aws-cdk-lib
  dependency-version: 2.261.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
- dependency-name: tsx
  dependency-version: 4.23.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Adam Moussa <166072409+amoussa1229@users.noreply.github.com>
2026-07-06 21:20:34 +00:00
dependabot[bot]
969a3f34e1
chore(deps-dev): bump vitest from 3.2.7 to 4.1.10 in /infra (#9)
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 3.2.7 to 4.1.10.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.10/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 4.1.10
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 17:19:11 -04:00
Adam Moussa
0b1fe539d2
chore(deps): patch vitest/vite advisories and add dependabot (#6)
Some checks are pending
deploy / deploy (push) Waiting to run
Upgrade vitest to 3.2.7 across the monorepo, override vite to 6.4.3 for
the high-severity fs.deny advisory, and add weekly grouped Dependabot for
npm workspaces and GitHub Actions.
2026-07-06 16:54:50 -04:00
Adam Moussa
a722d62f02
docs: add sh-mcp-auth deploy runbook (#5)
Some checks failed
deploy / deploy (push) Has been cancelled
First-time deploy procedure for the Phase 2a Cognito auth substrate: the five
one-time prerequisites (CDK bootstrap, the two Google secrets with exact JSON
shapes, the githubdeploy-sh-mcp OIDC role, the managed Google Groups), the
synth → diff → watched manual first deploy → CD hand-off flow, post-deploy
validation (ESSENTIALS + V2 trigger, finance client ceiling, group-sync run,
deny-list hard-revocation smoke test), and rollback/teardown (RETAIN tables;
0a spike teardown deferred until sh-mcp-auth is validated).

Notes that CD (deploy.yaml) is already wired and red on every merge until the
OIDC deploy role exists, and that no Cognito hosted-UI domain ships in 2a
(OAuth code flow deferred to 2b surface wiring).
2026-07-02 15:53:22 -04:00
Adam Moussa
b5e604dabe
Phase 2a: Cognito auth substrate (CDK) + pre-token & group-sync Lambdas (#4)
Some checks failed
deploy / deploy (push) Has been cancelled
* Phase 2a: Cognito auth substrate (CDK) + pre-token & group-sync Lambdas

Stands up the real AWS auth broker the servers already validate against
(SH_MCP_ENV=aws), surface-agnostic. Nothing deployed yet (gated on Google
secrets); CI synthesizes the stack.

infra/ — root CDK app, stack sh-mcp-auth:
  - Cognito user pool, ESSENTIALS feature plan (required for the V2 pre-token
    trigger), Google external OIDC IdP (client_id/secret resolved from Secrets
    Manager at deploy via CFN dynamic reference, never inlined).
  - Resource servers + per-tier app clients whose AllowedOAuthScopes ARE the
    trust-tier boundary: ops=(read,tasks), exec=(ops+gmail/calendar, NO finance),
    finance=(finance:read ONLY, 15-min access TTL). offline refresh 30d.
  - Cognito groups sh-mcp-ops/-assistant/-finance/-admin.
  - sync-state + deny-list DynamoDB tables (overrideLogicalId pinned so a future
    refactor cannot replace+drop them; deny-list TTL attr 'expiresAt').
  - Least-priv IAM (no wildcard action/resource; Google SA secret grant scoped to
    the one secret), arm64 Lambdas, explicit 60-day log groups, alarms on the
    seahaven-alarm-topics CMK (ALARM-state actions only, two-alarm group-sync).

auth/pre-token-gen — SUPPRESS-ONLY V2 Lambda. Maps Cognito group entitlement to
  scopesToSuppress; NEVER scopesToAdd a tier scope (AllowedOAuthScopes stays the
  ceiling). Reads last_successful_sync; fail-closed to base ops:read when stale.

auth/group-sync — mirrors Google Group membership into Cognito groups every 5 min
  (jose-signed SA JWT -> Directory API, no googleapis dep); writes the freshness
  marker ONLY on full success so a partial failure keeps the pre-token Lambda
  failing closed.

37 new tests (suppress-only policy, fail-closed, reconcile diff, 16 CDK
assertions incl. Essentials/V2/per-client-scope/no-wildcard-IAM). 448 total pass;
tsc -b + infra typecheck + cdk synth + prettier clean; CI run-cdk-synth re-enabled.

App-client callback URLs are a context placeholder pending the surface decision.
Confluence map (1540098) + project memory updates owed once this deploys.

* Phase 2a: harden auth substrate per security-review + IAM cross-review

Both mandatory gates run on the 2a diff. GPT-4.1 IAM/Lambda cross-review: the
suppress-only invariant is now an executable fail-closed guard (a future edit
that sets scopesToAdd throws → no token minted). /sh-security-review fan-out +
proof-or-kill verifier: PASS (0 confirmed critical/high). The verifier refuted
the two "high" candidates (the email-case revocation "bypass" is symmetric — the
add path uses the same lowercasing filter, so an un-removable user could never
have been added; the empty-directory purge is a non-200 throw → stale marker →
fail closed). Three confirmed findings remediated:

- C2 (deny-list was inert): the sh-mcp-deny-list table was provisioned and
  documented as "hard revocation" but no code read it. The pre-token Lambda now
  reads it on every mint (DENY_LIST_TABLE env + grantReadData) and strips a
  deny-listed sub to NO tier scopes, ahead of the next group sync. Fail-OPEN on
  a DDB read error (logs deny_list_read_failed) so a blip can't lock everyone
  out — group membership + its fail-closed 30-min window stay authoritative.
- C5 (finance 30-day refresh nullified the 15-min access TTL): refresh window is
  now per-tier; finance caps at 8h, ops/exec keep 30d.
- C7 (nested Google-group members silently dropped): listGroupMembers now sets
  includeDerivedMembership and skips non-USER rows, honoring the documented
  "nested resolved" contract instead of pushing a phantom group address.

Also corrects the sync.ts comment that overstated fail-closed as instantaneous
(it is bounded by MAX_SYNC_AGE_MS). +8 tests (deny-list unit, hard-revocation
handler path, finance refresh window, deny-list env wiring); 456 pass. tsc -b,
cdk synth, prettier, eslint all clean.
2026-06-26 14:33:46 -04:00
Adam Moussa
22c09e99fe
Phase 1: runnable MCP + OpenAPI servers (ops + finance) (#3)
Some checks are pending
deploy / deploy (push) Waiting to run
* Add shared transport: dispatch, MCP + OpenAPI adapters, local auth

Add the single authoritative tool-execution path (executeTool) plus the two
universal interfaces over it (design.md §2.5, §7.3):
- dispatch.ts: scope enforcement, ajv input validation, rate limiting, finance
  egress redaction (redactDeep), and structured audit emission on one path.
- audit.ts / rate-limit.ts: injected AuditLogger + RateLimiter abstractions.
- mcp.ts: low-level MCP Server with scope-filtered tools/list (tool-hiding) and
  tools/call routed through executeTool.
- http.ts: Express host mounting /mcp, /openapi.json, POST /tools/:name, /healthz.
- openapi.ts: buildOpenApiDocument wraps the existing path generator into a full
  OpenAPI 3.1 document.
- local-auth.ts: LocalAuthProvider (dev bearer tokens) that refuses to construct
  outside SH_MCP_ENV=local and enforces audience binding (design.md §3, §6).

* Add in-memory dev clients; make package tool exports lazy

Add an in-memory Client implementation per integration package (seeded fake
data, no network) selected when SH_MCP_ENV=local (build-plan §4). Gmail/calendar/
tasks dev clients partition by ctx.sub; payments/qbo seed sensitive-looking
fields so the redaction egress path has real targets to mask.

Make the eager default-tool exports in tasks/reminders/qbo LAZY (getDefaultTools)
so importing a package barrel no longer constructs an AWS client at module load
(build-plan §7 'no I/O at import time') — the previous eager construction broke
server startup. Fix payments tsconfig rootDir (src, was '.') so its declarations
resolve under dist/index.d.ts like the other 8 packages.

* Add runnable sh-mcp-ops and sh-mcp-finance servers

Two thin composition-root servers over the shared transport (design.md §3):
- ops: internal-data, knowledge-base, google-maps, gmail, calendar, tasks,
  reminders. finance: qbo, payments (audited + redacted on egress).
- config from env only (no hardcoded ids/issuer/tables); SH_MCP_ENV selects
  LocalAuthProvider + dev clients (local) vs CognitoAuthProvider + real stubs
  (aws). Finance applies the 15-min finance-token TTL ceiling (design.md §2.5).
- index.ts is the only place .listen() is called; a Lambda handler placeholder
  is exported but not depended on.
- synth-only CDK stubs (no real IAM/Cognito/WAF) so 'cdk synth' has a valid app
  (build-plan §6); READMEs document local run, dev tokens, curl, MCP Inspector.

* Add security-weighted test suite + coverage gate; wire tooling

Add tests for the highest-risk surface (build-plan §5, design.md §7.3):
tool-hiding, server-side scope enforcement (incl. forced hidden calls),
audience binding, input-schema validation, finance redaction on egress, audit
emission with hashed args, prompt-injection regression (tool output is data),
rate limiting, MCP conformance (in-memory transport round-trip), OpenAPI 3.1
validity, and local-auth safety. Add HTTP integration tests (supertest) for both
servers and per-package dev-client tests. 405 tests pass.

Wire the coverage gate into vitest.config.ts: 80% overall, with per-file
thresholds on the auth + dispatch crown jewels; exclude deferred real client
stubs, entrypoints, cdk apps, and aws-only config from the gate (documented).
Extend eslint flat config + add .prettierignore to cover servers/. Commit the
updated package-lock.json.

* Suppress pre-existing dev-tooling + out-of-scope scanner findings

Add written-justification suppressions for the 4 confirmed crit/high pre-push
scanner findings, none of which are in this PR's Phase 1 production code:
- npmaudit vitest / @vitest/coverage-v8 / vite: dev/test-only deps that never
  run in the deployed server/Lambda runtime (pins carried from Phase 0b;
  Dependabot will bump).
- gitleaks docs/agentforce-plan.md secret: that file is not on this branch and
  not in this changeset; flagged for the maintainer to scrub on its own branch.

The deep agentic /sh-security-review (required for this auth/authz-touching PR)
was NOT run by the agent and is flagged outstanding in the PR body.

* Address CodeQL findings: bound ajv error work + edge rate limiting

GHAS code-scanning alerts on this PR:
- dispatch.ts (js/resource-exhaustion): ajv ran with allErrors:true on
  untrusted input, letting a crafted payload force unbounded error
  enumeration. Switch to allErrors:false (default) so validation
  short-circuits on the first failure; the 400 still names that path.
- http.ts (js/missing-rate-limiting): the authenticated routes (/mcp,
  /tools/:name) had no edge throttle — auth/JWT verification ran on every
  request before the per-sub dispatch limiter could apply. Add an IP-keyed
  express-rate-limit in front of authenticate (120/60s default, configurable),
  returning the standard 429 shape. Defense-in-depth over the per-sub +
  per-tool limiter in executeTool; API GW/WAF remains the production edge.

Tests: +2 cases proving the edge limiter throttles before auth (429, not
401) on /tools and /mcp. 407 pass; tsc/eslint/prettier clean.

* Fix polynomial ReDoS in Bearer-token extraction (CodeQL js/polynomial-redos)

extractBearerToken matched /^Bearer\s+(.+)$/ — \s and . both match a space,
so the two quantifiers overlap and a crafted header can drive polynomial
backtracking. Require the capture to start with a non-whitespace char
(/^Bearer\s+(\S.*)$/), removing the ambiguity → linear match. Behavior is
unchanged for real tokens; +2 regression tests.

* Harden auth + finance redaction (sh-security-review confirmed mediums)

Two confirmed medium findings from the agentic security review:

- Fail-open SH_MCP_ENV: config defaulted to 'local' when the var was unset,
  so a deploy that forgot SH_MCP_ENV=aws would silently run LocalAuthProvider
  and accept static dev bearer tokens (dev-finance-admin -> finance:admin).
  Now fail-closed: SH_MCP_ENV must be explicitly 'local' or 'aws' or the
  server refuses to start. Plus an independent guard in LocalAuthProvider
  that refuses to construct in an AWS runtime (AWS_LAMBDA_FUNCTION_NAME /
  AWS_EXECUTION_ENV present), regardless of the env flag.

- Finance egress redaction gap: redactDeep only wholesale-masked a sensitive
  key when its value was a scalar; an object/array under a sensitive key was
  recursed into, letting a bare nested value (e.g. {account:{number:...}})
  escape the keyword-gated pattern matcher. Now the entire subtree under a
  sensitive key is masked. No current finance tool emitted such shapes (all
  flat strings), so this closes a latent hole in the universal safety net.

+4 tests (subtree redaction, AWS-runtime guard). 411 pass; coverage gate green.

Review also produced lows (memo free-text digits, unsalted argsHash,
unauth /openapi.json by-design, session-cap no-reset by-design) tracked
separately; 0 confirmed critical/high — review verdict PASS.
2026-06-26 13:33:21 -04:00
Adam Moussa
a28e22bb91
Add Agentforce migration & architecture plan (#1)
* Add Agentforce migration & architecture plan

Decision document mapping the sh-mcp design.md substrate onto Agentforce:
3-agent roster (Ops/Finance/Lauren Exec) on trust-tiered MCP servers,
per-user OAuth via Cognito, Data Library/retriever design replacing the
Bedrock KB, model/DX/Testing-Center plans, cutover phasing, and an
11-item capability-gap register.

https://claude.ai/code/session_01BvKGBQ4ek6JRZVkFicZuw6

* Resolve open decisions: per-user auth pattern + reasoning model

Fold Adam's decisions and deep-research (wf_1bf9e142) outcomes into the plan:
- D4: ES/Apex actions + Per-User OAuth Browser Flow External Credential -> Cognito;
  native remote-MCP connector off the per-user hop (Beta, per-user binding unconfirmed).
- D5: AWS-Hosted Claude as the planner; BYOLLM ruled out (custom-action-only, routes
  through SF Models API/Trust Layer). Guardrail moves to the MCP/action layer (revises D11).
- D1 accepted; D3 accepted; G8 corpus authoring deferred (fund when needed).
- Resolve gaps G1/G2/G6; add §0.1 resolutions and a Phase-0 verify-in-org gate.

* Add D12: OpenAPI-first, MCP-ready transport-agnostic core

Tool handlers + shared auth/scope/PII/audit guard built independent of wire
protocol; OpenAPI adapter shipped now (Agentforce External Service actions, Apex
only for shaping); MCP adapter deferred to a named trigger (real IDE/Claude Code
workflow, or native remote-MCP per-user GA). Update decision log, §0.1 transport
architecture block, §1h test split (OpenAPI contract now, MCP conformance deferred),
and §4 deliverables (annotate design.md §4; transport-agnostic core story).

* Remediate sh-plan-review findings (B1-B5, F1-F7, NITs, Qs)

Address the Fable plan-review gate (REQUEST CHANGES):
- B1: move ALL teardown to Phase 4 + shared-consumer audit; notion-sync dual-feeds;
  rollback never targets a deleted/starved resource.
- B2: propagate the §0.1 resolutions through D5/D11/§1d/§2.x/§3/G6/G9/§6 (model,
  guardrail, dropped ~30% claim, Phase-0 'verify' not 'decide').
- B3: pin D12 to one facade per trust tier (per-server Cognito audience at the edge);
  reconcile the §1h list_tools test (deferred with MCP adapter).
- B4: specify per-agent External Credential + Cognito app client minting only its tier's
  scopes; add the token-layer trifecta test.
- B5: split Phase 0 (0a auth spike gates 0b); add custom-Bolt fallback; relabel G1 as
  mitigation-chosen/unverified.
- F1-F7: Testing-Center identity (G12); design.md amendments reframed (F2); sh-agentforce
  new-repo checklist + cd-sfdx JWT-key auth (F3); platform-build phase (F4); Salesforce
  data-processor gap (G13/F5); memory+README obligations (F6); per-user visibility
  degradation (G14/F7).
- NITs: S3->Data Cloud ingestion pinned; facade+notion-sync ALARM monitoring; DevName
  naming boundary. Qs Q1-Q3 registered as G15 + verify items.
- §0.3 remediation log + gap count 11->15.

* Fold in cross_reviewer (GPT-4.1) auth/trifecta findings

Cross-family review caught defense-in-depth gaps:
- CR-1: validate aud at edge AND server-side (per-tier authorizer doesn't replace
  design.md §2.5 server enforcement); alarm on wrong-audience tokens.
- CR-6: finance-audience token must not reach Gmail/Calendar even with a Google token.
- CR-2: verify+enforce received sub is the Google Workspace sub, not a Salesforce id.
- CR-3: pre-token Lambda fails closed on cross-audience scope.
- CR-5: pre-token + group-sync are the auth SPOF — alarms + group-claim freshness bound.
- CR-4/CR-7: restrict per-client Cognito scopes; WAF is defense-in-depth only.
Reflected in §0.1 B3/B4, §1h tests, §4 monitoring, §5 cross-review log.

* Remediate Fable round-2: BLOCK-1 credential mechanism + residual fixes

- BLOCK-1: invert the token-layer trifecta layering — per-app-client AllowedOAuthScopes
  is the PRIMARY vendor-supported boundary (Cognito won't issue out-of-tier scopes
  regardless of group union); pre-token suppression is a fail-closed backstop; the
  aud/authorizer mechanism is flagged unverified-load-bearing and added to the §6 verify
  gate (#8); fix the design.md §2.3 misattribution + add the amendment to §4.
- FIX-1: §1f notion-sync dual-feeds via S3 (was 'repointed instead of').
- FIX-2: split Phase 0a exit gate into fatal vs decision-input (Testing-Center identity).
- FIX-3: remove stale 'boundary stays in infrastructure' phrasing (server is the boundary).
- FIX-4: bound parallel-run double-spend (G9 + Phases 1-3 time-box).
- FIX-5: specify minimal throwaway 0a kit + Slack-plan dependency.
- FIX-6: remove dangling (O3); D3 recorded as accepted.
- NITs: severity arithmetic, Ops welcome no longer oversells tasks, wrong-audience alarm
  named, flip-point clarified. Q1 fallback scope + Q2 freshness-bound documented.

* Fold in Gemini (round-3, third model family) auth findings

- Gemini-BLOCK-1: AllowedOAuthScopes strictly filtering a V2 pre-token Lambda's output
  is unverified -> fatal Phase-0a check (§6 #8a); if it fails, pre-token fail-closed
  becomes the primary boundary.
- Gemini-BLOCK-2: Cognito access tokens carry client_id/scopes, not native aud -> align
  §1h + facade/server checks to client_id allow-list / scope-prefix audience proxy.
- Gemini-Q: 0a spike must run on production-equivalent Enterprise Grid + real licenses.
- Gemini-NIT (path-corrected): project memory is a private store at ~/.claude/.../memory/,
  not the repo and not Gemini's own ~/.gemini path.
Three model families now converge: structural plan sound; only open risk is the auth
token-mint mechanism, fully spike-gated in Phase 0a.

* Fold in Gemini round-4 CI/CD + decoupling findings (with corrections)

- ARM64 markers (enable-qemu both files + platform:LINUX_ARM64) for Docker-bundled tier
  tasks -> Phase 0b build + exit (per reference_cicd_arm64_qemu).
- Node 24 / workflows: sh-agentforce keeps thin ci.yaml/deploy.yaml callers but they call
  the new cd-sfdx, NOT the AWS CDK templates (corrected the reviewer's framing).
- CR-1 decoupling: client/audience matrix injected as config (SSM/CDK env), not hardcoded
  into the transport-agnostic core; corrected reviewer's 'Secrets Manager' -> SSM (client_ids
  are non-sensitive). §6 #8d.
- Role isolation: new isolated githubdeploy-sh-agentforce (never reuse sh-mcp role), scoped
  only to read the JWT secret since deploy target is Salesforce not AWS.
Reviewer applied AWS/CDK conventions to a Salesforce-deploying repo; folded in with corrections.

* Fold in Phase-0a research findings (wf_3e88d8a8) — design changes + new top risk

Research resolved the doc-answerable 0a unknowns before the live spike:
- Finding 1: AllowedOAuthScopes does NOT cap a pre-token V2 Lambda's scopesToAdd ->
  redesign to a SUPPRESS-ONLY Lambda (keeps AllowedOAuthScopes as the real per-tier ceiling).
- Finding 2: V2 needs Essentials plan (default; Lite ignores it) — negligible cost.
- Finding 3: Cognito access tokens carry client_id/scopes, no aud -> client_id allow-list +
  scope-prefix as the audience proxy (resolves the §8c unknown).
- Finding 4 (NEW CRITICAL G16, now THE top risk): Employee-Agent callouts may carry SERVICE
  identity, not the user's -> 0a fatal #1; fallbacks MuleSoft RFC 8693 / signed header / Bolt.
- Finding 5: per-user actions UNTESTABLE via batch Testing Center -> scripted interactive
  parity sessions (G12 resolved).
- Finding 6: all-staff agent not free (Flex Credits / $125-user add-on) -> G9 cost model.
- Finding 7: model_config may allow BYOLLM as a per-agent planner -> reopen as verify (upside).
Added §0.4 findings record; gap count 15->16; verify items reordered (G16 = fatal #1).

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-26 12:47:04 -04:00
Adam Moussa
0d1fefb326
Phase 0b slice: monorepo scaffold + @sh-mcp/shared core + integration packages (#2)
Some checks are pending
deploy / deploy (push) Waiting to run
* Phase 0b slice: monorepo scaffold + shared core + integration packages

The 0a-INDEPENDENT code slice (one-shot via af-0b-package-slice workflow: Haiku
scaffold + Sonnet packages, Sonnet fix-to-green). Nothing deploys; no CDK/servers.

- Monorepo scaffold: npm workspaces, strict TS (NodeNext), vitest (80% gate),
  eslint 9 flat config, prettier; ci.yaml/deploy.yaml callers (Node 24, enable-qemu).
- @sh-mcp/shared: transport-agnostic core — Scope/AuthContext/ToolDef, ToolRegistry,
  redact()+maskValue() (PII), OpenAPI 3.1 generator. AUTH STUBBED behind an AuthProvider
  interface (TODO auth-layer-0a); JWT/aud/client_id/JWKS/deny-list deferred per design.md §2.
- 9 integration packages (qbo, google-maps, internal-data, payments, knowledge-base,
  gmail, calendar, tasks, reminders): tools against shared, external deps mocked behind
  injected client interfaces; finance handlers call redact().

Verified green: tsc -b clean, vitest 245/245, eslint 0 errors. Auth mechanism intentionally
deferred until the 0a spike resolves it (G16/§0.4).

* Complete Cognito auth provider + Phase 1 build brief

Finish the WIP CognitoAuthProvider (client_id allow-list as audience
boundary, finance TTL ceiling, deny-list, scope-prefix stripping) with
its test suite, and check in docs/build-plan-phase-1.md so the Phase 1
work has its governing brief in-tree (design.md §2.5).

* ci: disable cdk synth for Phase 0b (no CDK app yet)

The reusable ci-typescript-cdk workflow defaults run-cdk-synth: true, but
the Phase 0b package scaffold has no cdk.json or stacks, so cdk synth fails
with '--app is required'. Disable it here; Phase 1 re-enables it with the
server CDK stubs.
2026-06-26 12:42:17 -04:00
3e2d99a1eb Initial commit: sh-mcp design and plan
Design doc for the Sea Haven MCP platform that replaces seahaven-slack-bot
and exec-aide: trust-tiered MCP servers, Google-SSO via a Cognito broker,
TypeScript monorepo. Cross-review (cross_reviewer/GPT-4.1) folded in.

Status: planning only, nothing built.
2026-06-09 19:25:24 -04:00