Teams
Invite people, pick a role, switch env — without writing IAM JSON.
What you get
| Piece | Behavior |
|---|---|
| Org | Owns one project name; solo users get a personal org automatically |
| Env | First-class dev / staging / prod → utopia:env on create/list/destroy |
| Roles | admin · developer · read-only — enforced on the API, not just the UI |
| Audit | Append-only: who invited, created, destroyed, changed roles |
| Budgets | Soft monthly USD cap per project/env; uses Cost Explorer or says COST_UNAVAILABLE |
| Pricing | CLI free forever · dashboard free solo · teams per-seat (stub entitlements) |
Roles (plain language)
| Role | Can | Can’t |
|---|---|---|
| read-only | Inventory, logs, costs, redacted secrets, audit | Create/destroy/reveal/force/invite |
| developer | Create/destroy in project+env, reveal secrets, dry-run | Force cross-project, manage members |
| admin | Developer + force + invites + budgets + role changes | Bypass confirms or mint long-lived keys |
Destroy/create still need the same consequence-named confirm as the CLI. Teams never weaken that.
Store choice
Team data (orgs, members, invites, audit, budgets) persists in Bun SQLite (.awsutopia/data/teams.sqlite by default). Same shape can move to Cloudflare D1 later — documented so we’re not inventing a third model.
bash
bun run dashboard:api # opens SQLite + AssumeRole API
bun run dashboard:dev # Teams / Audit / Pricing pagesInvites
Admins invite by email → pending → accept with the invite id. Email delivery is mocked in CI/dev (you still get a real invite row).
Pricing copy
- CLI — free forever
- Dashboard solo — free
- Teams — per seat (entitlement stub until billing lands)