Skip to content

chore: promote develop to master (gateway account roles + routing fixes)#63

Merged
aabrius merged 12 commits into
masterfrom
develop
Jun 16, 2026
Merged

chore: promote develop to master (gateway account roles + routing fixes)#63
aabrius merged 12 commits into
masterfrom
develop

Conversation

@aabrius

@aabrius aabrius commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Promotes the multi-tenant gateway-account-roles work + the routing/config fixes from develop to master.

Includes (12 commits):

Merging triggers release.yml → the changesets Release PR (@etus/bhono minor bump).

aabrius and others added 12 commits June 10, 2026 12:44
"Tool:*" is not valid allow-rule syntax, so /doctor reported all seven
entries as skipped. Edit/Write/NotebookEdit become bare tool names
(auto-approved, matching the original intent); the broad Bash/WebSearch/
WebFetch wildcards are dropped so Bash stays governed by the specific
command rules, and the dead MultiEdit entry is removed.
The root .npmrc gained link-workspace-packages=true in e5375c3 but the
mirrored copy under packages/bhono-app/templates/base/ was never
re-synced, so the pre-push template sync check fails on any push.
- ACCOUNT_ROLE_MAP: remove wildcards (admin was '*') and destructive perms.
  The package unions the map across ALL of a user's gateway accounts, so a
  wildcard let admin-on-any-account pass every guard app-wide. Now bounded +
  non-destructive; precise per-account authority stays in requireGatewayAccountRole.
- matrix.test: anchor the role-vocabulary drift guard to @etus/auth's exported
  ACCOUNT_ROLES (was a tautological literal copy); add no-wildcard + no-destructive
  invariants.
- Regenerate docs/openapi.json + src/shared/types/api.ts (now include /api/me).
- SETUP-GUIDE: document the org-level union + no-wildcard rule + that local member
  removal is not an authz kill-switch while gatewayAuthority is on.
…review)

Adds the two regression tests flagged by the PR #62 review panel.

Cross-account over-grant (tests/unit/server/auth/matrix.test.ts):
drives @etus/auth's REAL mapAccountRolesToPermissions over ACCOUNT_ROLE_MAP
and asserts that admin-on-any-account (and super-admin, treated as admin
everywhere) resolves to a bounded permission set — never '*' nor a
destructive perm (resources:delete / account:delete / billing:manage).
Because the package UNIONS the map across every gateway account a user
holds, this pins the conservative map: a regression that re-introduces a
wildcard/destructive grant fails here.

/api/me auth gate (tests/integration/api/me.test.ts): builds the REAL app
via buildApp() and asserts GET /api/me returns 401 without a session,
exercising the production /api/* requireAuthContext gate. The existing unit
test mounts the me router bare, so it could not prove the gate exists. Also
asserts /health stays public in the same app to rule out a boot-failure
false positive.

Exports buildApp from src/server/index.ts so integration tests can drive the
full middleware stack. Synced to template (packages/bhono-app/templates/base).
…tests

Adds a complete multi-tenant scenario to validate the hybrid gateway-account-roles
model end to end, without a live gateway.

Server
- src/server/dev/gateway-scenario.ts: a dev-only, email-keyed fixture of gateway
  per-account roles (viewer/editor/manager/admin) + super-admin, incl. the
  cross-account over-grant case (multi@ = admin on Initech + viewer on Acme). The
  resolver is gated TWICE: ENVIRONMENT!=production AND ETUS_GATEWAY_MOCK — never prod.
- /api/me consults the mock before the real gateway resolution (src/server/routes/me).
- Enriched seed.ts with the scenario users + pending/suspended statuses.

Client
- New Workspaces page (src/client/routes/_authenticated/workspaces.tsx, in the nav)
  rendering useGatewayAccounts(): super-admin banner, a card per gateway account with
  its role badge + what it grants, and an empty state.

Tests
- unit: gateway-scenario resolver + fixture invariants; Workspaces route component.
- integration: /api/me drives the mock through the REAL middleware via buildApp()
  (login → gate → handler → scenario), incl. the over-grant + super-admin cases;
  local membership assignment (GET /accounts, PATCH role, invite) via the worker.
- e2e: Workspaces renders the gateway roles (mock on) + nav; Team page smoke.

Docs / hygiene
- SETUP-GUIDE: how to validate the UI locally — note the Vite plugin reads
  config/.dev.vars (next to wrangler.json), NOT the root .dev.vars.
- gitignore + sync-template exclude the generated seed.sql.

Synced to template (packages/bhono-app/templates/base).
…t SPA fallback)

Two pre-existing routing/config bugs found while validating the multi-tenant UI.

1. Worker routes shadowed by the SPA assets fallback (affects DEV and PROD).
   wrangler.json `assets.not_found_handling: single-page-application` serves
   index.html for any path NOT in `run_worker_first`, which only listed `/api/*`
   and `/auth/*`. So `/accounts`, `/audit`, `/invitations` and even `/health`
   returned the SPA HTML instead of the worker — confirmed against the deployed
   demo (GET /health → HTML). This broke the Team page (members/invitations could
   not load → the invite button stayed disabled) and health checks. Fix: add the
   worker-owned prefixes (exact + wildcard) to `run_worker_first`. Verified in dev:
   /health → JSON, /accounts → 401 JSON, and the Team invite/assignment E2E now
   passes end to end.

2. Dev `.dev.vars` was at the repo root, where the Vite dev server never reads it.
   wrangler + @cloudflare/vite-plugin only read `.dev.vars` from the Wrangler
   config's directory (config/), so `ENVIRONMENT=development` (and the gateway
   mock) at the root were silently ignored and `pnpm dev` ran as
   ENVIRONMENT=production. Relocated `.dev.vars.example` → `config/.dev.vars.example`
   (the idiomatic location, per Cloudflare docs) and updated the spec comments.

Synced to template (packages/bhono-app/templates/base).
feat(auth): consume gateway per-account roles (@etus/auth 0.9.1, hybrid model)
@aabrius aabrius merged commit 4471b54 into master Jun 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants