feat(security): commandsSyncKey is a required channel-config field (Guild C-2)

The slash-command sync secret now comes from
channels.fabric.commandsSyncKey (configSchema marks it required) and
is no longer read from FABRIC_COMMANDS_SYNC_KEY env. command-sync
resolves it from config and threads it into client.syncCommands;
when absent, sync is skipped with a clear warning. README updated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
h z
2026-05-16 18:44:25 +01:00
parent bb63a57384
commit ab126825ef
8 changed files with 63 additions and 15 deletions

View File

@@ -124,17 +124,17 @@ export class FabricClient {
guildEndpoint: string,
guildToken: string,
commands: unknown[],
syncKey: string,
): Promise<unknown> {
// Guild C-2: when the operator sets a shared sync key on both sides
// (FABRIC_COMMANDS_SYNC_KEY here / FABRIC_BACKEND_GUILD_COMMANDS_SYNC_KEY
// on the guild), the catalog write is restricted to this plugin.
const key = process.env.FABRIC_COMMANDS_SYNC_KEY;
// Guild C-2: the shared key is sourced from the channel config
// (channels.fabric.commandsSyncKey) and must equal the guild's
// FABRIC_BACKEND_GUILD_COMMANDS_SYNC_KEY for the catalog write.
return this.req(
'PUT',
`${guildEndpoint}/api/commands`,
guildToken,
{ commands },
key ? { 'x-commands-sync-key': key } : undefined,
syncKey ? { 'x-commands-sync-key': syncKey } : undefined,
);
}