콘텐츠로 이동

Guild Schema

guild schema 는 Umbra 가 관리하는 Discord 길드 메타데이터와 길드별 설정을 저장한다.

Schema purpose

  • PostgreSQL schemaguild
  • Corresponding domainengine/guild/
  • sqlc packagedb/queries/guild/

Guild 는 License, Subscription, Snapshot, Member 등 많은 도메인이 참조한다. 안정적 키와 slug 관리가 핵심.

Tables

guild.guilds

Discord 길드의 Umbra 내부 표현.

CREATE TABLE guild.guilds (
    id                 UUID PRIMARY KEY,
    discord_guild_id   TEXT NOT NULL,
    name               TEXT NOT NULL,
    icon_hash          TEXT,
    slug               TEXT NOT NULL,
    owner_user_id      UUID REFERENCES identity.users(id),
    owner_discord_id   TEXT NOT NULL,
    bot_installed_at   TIMESTAMPTZ NOT NULL,
    bot_removed_at     TIMESTAMPTZ,
    deleted_at         TIMESTAMPTZ,
    created_at         TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at         TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT guilds_discord_guild_id_unique UNIQUE (discord_guild_id),
    CONSTRAINT guilds_slug_unique UNIQUE (slug),
    CONSTRAINT guilds_slug_format CHECK (slug ~ '^[a-z0-9-]{3,30}$')
);

Columns

Column Type Constraints Description
id UUID v7 PK Umbra 내부 Guild ID
discord_guild_id TEXT UNIQUE, NOT NULL Discord snowflake
name TEXT NOT NULL 길드 이름 (캐시)
icon_hash TEXT Discord icon hash
slug TEXT UNIQUE, NOT NULL 웹 조인 URL 용
owner_user_id UUID FK nullable identity.users.id, Owner 가 Umbra 가입 안 했으면 NULL
owner_discord_id TEXT NOT NULL Discord user snowflake (항상 저장)
bot_installed_at TIMESTAMPTZ NOT NULL 최초/최근 봇 설치 시각
bot_removed_at TIMESTAMPTZ 봇 강퇴 시각. 재설치 시 NULL 로 복원
deleted_at TIMESTAMPTZ Discord 길드 자체 삭제 시
created_at TIMESTAMPTZ NOT NULL
updated_at TIMESTAMPTZ NOT NULL

Indexes

Index Columns Purpose
guilds_pkey (id) PK
guilds_discord_guild_id_unique (discord_guild_id) Discord ID 조회
guilds_slug_unique (slug) 웹 조인 slug 조회
guilds_owner_user_idx (owner_user_id) WHERE owner_user_id IS NOT NULL owner 의 길드 목록
guilds_active_idx (id) WHERE bot_removed_at IS NULL AND deleted_at IS NULL 활성 길드

Foreign keys

Column References On delete
owner_user_id identity.users(id) SET NULL (owner 가 탈퇴해도 Guild 유지)

Invariants

  • slug 형식: 소문자 영숫자 + 하이픈, 3~30자 (CHECK 제약)
  • bot_removed_at 이 NULL 이 아니면 실제 봇 부재 상태
  • deleted_at 설정 시 복구 불가능 상태

guild.guild_configs

길드별 Umbra 설정. Guild 와 1:1.

CREATE TABLE guild.guild_configs (
    guild_id                    UUID PRIMARY KEY REFERENCES guild.guilds(id),
    notification_channel_id     TEXT,
    web_join_auto_role_id       TEXT,
    antinuke_enabled            BOOLEAN NOT NULL DEFAULT FALSE,
    antinuke_auto_action        BOOLEAN NOT NULL DEFAULT FALSE,
    antinuke_thresholds         JSONB NOT NULL DEFAULT '{}',
    updated_at                  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

Columns

Column Type Constraints Description
guild_id UUID PK, FK guilds.id
notification_channel_id TEXT Umbra 알림 채널 (Discord channel snowflake)
web_join_auto_role_id TEXT 웹 조인 시 자동 부여 역할
antinuke_enabled BOOLEAN NOT NULL Pro 이상에서만 true 가능
antinuke_auto_action BOOLEAN NOT NULL Enterprise 에서만 true 가능
antinuke_thresholds JSONB NOT NULL 커스텀 임계값 (비어있으면 기본값 사용)
updated_at TIMESTAMPTZ NOT NULL

Foreign keys

Column References On delete
guild_id guild.guilds(id) CASCADE

Invariants

  • antinuke_auto_action = true 이면 antinuke_enabled = true 필수 (애플리케이션 레벨)
  • Plan 불일치 체크 (Free 에서 antinuke_enabled = true 불가) 는 Licensing 에서 강제
  • antinuke_thresholds JSONB 예시:
{
  "mass_role_delete": { "count": 5, "window_seconds": 300 },
  "mass_channel_delete": { "count": 3, "window_seconds": 300 },
  "mass_kick": { "count": 10, "window_seconds": 300 }
}

Relationships

erDiagram
    GUILDS ||--|| GUILD_CONFIGS : has
    GUILDS ||--o{ MEMBERS : contains
    GUILDS ||--o{ LICENSES : licensed
    GUILDS ||--o{ SNAPSHOTS : backed_up
    USERS ||--o{ GUILDS : owns

Cross-schema references

From To Semantics
guild.guilds.owner_user_id identity.users.id Owner
member.members.guild_id guild.guilds.id 멤버가 속한 길드
licensing.licenses.guild_id guild.guilds.id 길드 권한
billing.subscriptions.guild_id guild.guilds.id 길드 구독
recovery.snapshots.guild_id guild.guilds.id 길드 스냅샷
recovery.antinuke_incidents.guild_id guild.guilds.id AntiNuke 감지 이력

Query patterns

  • GetGuildByID — 내부 PK 조회
  • GetGuildByDiscordID — Discord ID 로 조회
  • GetGuildBySlug — 웹 조인용
  • InsertGuildOnBotInstall — 봇 설치 시 upsert
  • MarkBotRemoved — 강퇴 처리
  • MarkGuildDeleted — 길드 삭제 처리
  • UpdateOwner — owner 변경
  • UpsertGuildConfig — 설정 저장
  • ListActiveGuildsOfUser — 대시보드의 내 길드 목록

Data retention

  • Guilds — 영구 보관 (감사 목적). deleted_at soft delete.
  • Guild configs — Guild CASCADE
  • 봇 강퇴 후에도 60일간 License/Subscription 을 suspend 상태로 유지 → 재설치 시 복구 가능

Migration history

Date Change Rationale
2026-04-xx 초기 스키마 MVP

See also

  • domain/guild.md — Guild 도메인
  • data/schema-overview.md — 전체 스키마 관계
  • data/licensing-schema.md — 참조하는 License
  • adr/0020-postgres-schema-per-domain.md — schema 분리