Skip to content

Storage Layout

KV Storage Layout

KV keeps only cache data and short-lived/ephemeral state. High-frequency writes (webhook dedup, delivery state, message tracking) live in D1 and only fall back to KV when D1 is unavailable or not yet migrated (see Storage decisions).

Key PatternValueTTL
config:routesRoute config cache (D1 d1_routes is authoritative)1 hour
config:groupsGroup config cache (D1 d1_groups is authoritative)1 hour
session:{id}Admin session { userId, login }7 days
token:{userId}{ userId, accessToken, expiresAt, refreshToken? }0.9 × token expiry
token-reverse:{sha256}User id for reverse lookup by token0.9 × token expiry
state:{hex}{ redirectTo, expiresAt, discordUserId?, telegramUserId?, telegramChatId? }600 seconds
invite:{token}{ groupId, role, expiresAt, createdBy, note? }7 days
invite:group:{id}Token index per group (keeps invite listing consistent)7 days
delivery:{provider}:{groupId}:{id}Webhook delivery dedup fallback (D1 dedup_keys is primary)7 days
delivery-state:{provider}:{groupId}:{id}Queue delivery state fallback (D1 delivery_state is primary)1 hour
queue:payload:{provider}:{groupId}:{id}Oversized webhook payload parked for the queue consumer (R2 is primary)1 hour
nonce:{nonce}Custom-webhook replay protection nonce (single use)600 seconds
tenant:{groupId}Per-group webhook secret (64-char hex, generated from the console)Permanent
msg:{routeId}:{key}:{target}Message id tracking fallback (D1 message_tracking is primary)1 day
cmd:guild:{id}Guild id whose commands were registered (dedup)Permanent
cmd:registered:globalGlobal command registration marker (dedup)1 day
config:discord-app-idCached Discord application idPermanent
i18n:{lang}Translation overrides merged on top of EnglishPermanent

D1 Storage Layout

The D1 database (DB binding, database webhooker) holds the source of truth for configuration, delivery logs and high-frequency ephemeral state:

TablePurpose
d1_groupsGroups (authoritative config, seeded from legacy KV config:groups)
d1_routesRoutes per group (authoritative config, seeded from legacy KV config:routes)
send_logsOne row per dispatch attempt (route id, event, target, ok/error, duration, error code, detail)
audit_logsOne row per admin operation (login/logout, group/route/member/invite changes)
dedup_keysWebhook delivery dedup (atomic INSERT ... ON CONFLICT UPSERT, key + expiry)
delivery_stateQueue delivery state (pending/processing/delivered/retrying/failed/dead)
message_trackingMessage id tracking for in-place updates (event_id + target_idmessage_id)
discord_linksMaps discord_user_idgithub_user_id for /gh Discord commands
telegram_linksMaps telegram_user_idgithub_user_id for /gh Telegram commands

audit_logs is pruned by the scheduled trigger after AUDIT_RETENTION_DAYS (default 90). The storage-prune task removes expired dedup_keys, delivery_state rows older than 7 days and message_tracking rows older than 30 days. See Logs for the log row fields.

R2 Storage Layout

R2 (PAYLOAD binding, bucket webhooker-payloads) stores oversized webhook payloads that are too large for a queue message or KV:

Object PatternPurposeRetention
webhooks/YYYY/MM/DD/<uuid>.jsonOversized payload parked for the queue consumerdeleted after dispatch

When the PAYLOAD binding is absent, oversized payloads fall back to the KV key queue:payload:{provider}:{groupId}:{id} (1 hour TTL).

Storage Decisions

  • D1 is authoritative for config and delivery metadata; KV holds only caches and short-lived state.
  • The canUseD1 probe (server/lib/storage/d1.ts, checks for prepare + batch) gates every D1 store: when D1 is unavailable or not yet migrated, all three high-frequency stores (dedup, delivery state, message tracking) transparently fall back to KV so behavior is unchanged during migration.
  • This keeps per-event KV writes near zero on the Workers Free plan (1,000 writes/day): dedup, delivery state and message tracking now write D1 rows instead (D1 Free allows 100,000 rows written/day).
  • R2's free tier (10 GB-month storage, 1M Class A ops/month) comfortably absorbs payload parking without touching the KV write quota.

Released under the MIT License.