You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(discord): add Terraform-managed base allowlist and admins (#34)
## Summary
Introduces a Terraform-managed baseline for Discord allowlists and admin
lists that cannot be removed via the management UI. This allows
operators to enforce a permanent floor of allowed guilds and admins
while still permitting the UI to manage additional entries.
## Key Changes
- **New `BaseDiscordConfig` type** in shared types representing the
read-only Terraform baseline (allowedGuilds and admins)
- **New DynamoDB row `BASE#discord`** written by Terraform on every
apply when any base list is non-empty, with three new Terraform
variables:
- `base_allowed_guilds`: Guild IDs permanently allowlisted
- `base_admin_user_ids`: User IDs with permanent admin privileges
- `base_admin_role_ids`: Role IDs with permanent admin privileges
- **New `getBaseDiscordConfig()` function** reads the BASE#discord row
with strongly consistent reads, returning an empty base when absent
- **New `getEffectiveDiscordConfig()` function** merges the Terraform
base and dynamic CONFIG#discord rows, deduplicating entries. This is now
used by both Lambda handlers for permission checks
- **Updated `DiscordConfigService`** to:
- Cache and load the base config separately with coalesced concurrent
reads
- Expose `getBaseConfig()` method for retrieving the base config
- Prevent removal of guilds/admins that exist in the Terraform base
(returns error with explanation)
- Include base lists in the redacted config sent to the web client
- **Updated REST API endpoints** to return both dynamic and base lists:
- `GET /discord/guilds` and `GET /discord/admins` now include
`baseGuilds` and `baseAdmins`
- `DELETE /discord/guilds/:guildId` returns 400 if the guild is in the
base config
- All mutation endpoints return the updated base lists alongside dynamic
lists
- **Updated Lambda handlers** (interactions and followup) to use
`getEffectiveDiscordConfig()` instead of `getDiscordConfig()` for
permission checks, ensuring base entries are always enforced
## Implementation Details
- Base config is cached in `DiscordConfigService` and invalidated
alongside the dynamic config
- Terraform resource uses `count` to skip creation when all base lists
are empty (UI-only deployments)
- Defensive parsing sanitizes malformed base data without throwing
- Web client can render base entries as locked/non-removable based on
the new `baseAllowedGuilds` and `baseAdmins` fields in the redacted
config
https://claude.ai/code/session_01GxkgH9sABMbHDCQqXehHP1
---------
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CLAUDE.md
+9Lines changed: 9 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -133,6 +133,15 @@ The full deploy IAM policy (`GameServerDeployAll`) lives in **`docs/setup.md`**
133
133
134
134
All resources inherit `default_tags` from `provider "aws"` (`Project = "game-servers-poc"`, `Environment = "poc"`, `ManagedBy = "terraform"`). For Cost Explorer breakdowns, the `Project` cost allocation tag must be activated manually in AWS Billing — this is a one-time console action, not Terraform-managed.
135
135
136
+
## Checklist for Terraform variable changes
137
+
138
+
Any time you add or remove Terraform variables, update **all four** of these in the same commit — failing to touch any one of them is a common oversight:
139
+
140
+
1.`terraform/variables.tf` — the variable declaration itself.
141
+
2.`terraform/terraform.tfvars.example` — a commented-out example entry with a short explanation so operators know how to use it.
142
+
3.`docs/docs/components/terraform.md` — the Variables table row.
143
+
4.`docs/docs/setup.md` — any relevant step in the setup guide (especially if the variable affects the Discord bot or a core workflow).
144
+
136
145
## Code & Test Conventions
137
146
138
147
-**Test names**: every `it(...)` case must read as a natural-language sentence starting with "should" — e.g. `it('should return null when state file is missing')`, not `it('returns null...')`.
0 commit comments