vibe-mod 실증 검증 보고서

Devvit 공식 문서(reddit/devvit-docs GitHub) verbatim cross-reference · 4 전문가 plan 클레임 전수 검증

분석: 2026-05-11 KST 소스: github.com/reddit/devvit-docs @ HEAD 중대 변경 1건: AI provider 아키텍처 전체: 실현 가능

1. 검증 요약

✅ vibe-mod는 Devvit에서 실현 가능합니다. 모든 핵심 primitive(triggers, redis, scheduler, settings, http fetch, reddit API 메서드)가 공식 문서로 verbatim 확인됨. 단, 2가지 critical 변경이 필요합니다.
🚨 결정적 발견 #1 — AI provider 정책 Reddit이 명시적으로 "At this time, the only AI providers we allow are OpenAI and Google Gemini... Requests to use any other AI provider will be denied."라고 룰화. → Claude Haiku 4.5 사용 불가. OpenAI gpt-4o-mini 또는 Gemini Flash로 pivot 필수.
⚠️ 결정적 발견 #2 — Devvit Web 패턴 원본 plan의 Devvit.addTrigger(...) closure 패턴은 구버전 @devvit/public-api. 현재 공식은 @devvit/webdevvit.json에 endpoint 선언 + Hono/Express HTTP 라우트로 처리. 아키텍처 다이어그램·코드 샘플 모두 업데이트 필요.

1.1 11개 클레임 검증 결과

#원본 plan 클레임판정verbatim 근거
1Devvit apps can call external HTTPS APIs via fetch"Make requests to allow-listed external domains"
2Claude Haiku 4.5를 LLM으로 사용"the only AI providers we allow are OpenAI and Google Gemini... Requests to use any other AI provider will be denied"
3onPostSubmit / onCommentSubmit 트리거 사용모든 트리거 verbatim enumerate
4Devvit Redis (1000 cmd/s, 500MB)+실제는 40,000 cmd/s (훨씬 관대), 500MB 스토리지 정확
5Sorted sets (zAdd/zRange) 지원verbatim — zAdd/zRange/zRem/zScore/zRank/zIncrBy/zScan/zRemRangeBy*
6Redis transactions (multi/exec/watch)verbatim — max 20 concurrent transactions, 5s timeout
7Scheduler cron + one-shotverbatim — scheduler.runJob({cron|runAt})
8Settings with isSecret (App scope)verbatim — global+subreddit scopes, isSecret 지원, CLI로만 설정 가능
9Reddit API: removePost, modMail.create, setUserFlair 등*메서드 전부 존재 (단 호출 패턴은 comment.remove() 객체-메서드형으로 변경)
10Devvit.addTrigger() closure-style handler⚠️구버전 패턴. 현 공식: devvit.json에 endpoint + HTTP route
11Devvit.addCustomPostType + addSettings⚠️현 공식은 devvit.json 설정 + 서버 endpoint 패턴

전체 판정: 9/11 fully feasible, 2/11 require pattern modernization. 컨셉은 흔들리지 않음.

2. 🚨 결정적 변경: Claude → OpenAI (또는 Gemini)

2.1 정책 verbatim

At this time, the only AI providers we allow are OpenAI and Google Gemini:

- api.openai.com
- generativelanguage.googleapis.com

Requests to use any other AI provider will be denied. — docs/capabilities/server/http-fetch-policy.md, section "AI providers"

2.2 LLM 재선정

모델속도비용 (입력/출력 per 1M tokens)JSON 모드판정
OpenAI gpt-4o-mini~3-5s$0.15 / $0.60response_format:{type:'json_object'}최우선 픽
OpenAI gpt-4o~5-8s$2.50 / $10고비용, 불필요
Gemini 2.0 Flash~2-4s$0.10 / $0.40 (대략)✅ structured output대안
Gemini 2.5 Pro~5-8s$1.25 / $5고비용
Claude Haiku 4.5~3-5s$1 / $5정책 거부

2.3 신규 비용 추정 (gpt-4o-mini 기준)

  • 1 룰 컴파일: 800 in + 400 out ≈ $0.00012 + $0.00024 = $0.00036 (≈ 0.36¢ → Haiku의 8배 cheaper)
  • 50 컴파일/sub/day × 30일 × 1,000 sub = 150만 컴파일 × $0.00036 = $540/월
  • 실제로 sub당 일평균 5 컴파일 정도면 → $54/월 (1,000 sub 운영)

2.4 영향 — 아키텍처 변경 사항

  • devvit.json: permissions.http.domains"api.openai.com" (또는 "generativelanguage.googleapis.com") 추가
  • global settings: openaiApiKey (isSecret: true). 공식 settings-and-secrets 문서에 정확히 이 패턴이 예시로 등장 → vibe-mod-shaped 사례
  • System prompt + JSON schema: OpenAI JSON 모드 활용 (Zod validator 그대로 유지)
  • 비용 절감: Anthropic보다 8배 저렴 → 50 compiles/day rate limit 완화 가능 (예: 100/day)

2.5 공식 sample — vibe-mod와 동일 패턴

// devvit.json (공식 settings-and-secrets.mdx 예시 — 그대로 차용 가능)
{
  "settings": {
    "global": {
      "openaiApiKey": {
        "type": "string",
        "label": "OpenAI API Key",
        "isSecret": true,
        "defaultValue": ""
      }
    }
  },
  "permissions": {
    "http": { "enable": true, "domains": ["api.openai.com"] }
  }
}

// server/index.ts (공식 예시 그대로)
import { settings } from '@devvit/web/server';

app.post('/api/generate', async (c) => {
  const apiKey = await settings.get('openaiApiKey');
  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
    body: JSON.stringify({ model: 'gpt-4o-mini', messages: [...] }),
  });
  ...
});

3. API / Primitive 검증 매트릭스

3.1 Triggers (verbatim from docs/capabilities/server/triggers.mdx)

vibe-mod에서 사용공식 이름판정
새 포스트 → 룰 평가onPostSubmit
새 댓글 → 룰 평가onCommentSubmit or onCommentCreate
리포트 임계점 → 액션onPostReport / onCommentReport
모드 액션 learning (v0.2)onModAction
최초 install 시 seedonAppInstall
버전 업그레이드 시 migrationonAppUpgrade

등록 방법 (verbatim):

// devvit.json
"triggers": {
  "onAppUpgrade": "/internal/on-app-upgrade",
  "onPostSubmit": "/internal/on-post-submit",
  "onCommentCreate": "/internal/on-comment-create"
}

// server/index.ts (Hono)
app.post('/internal/on-post-submit', async (c) => {
  const input = await c.req.json<OnPostSubmitRequest>();
  const post = input.post;
  const author = input.author;
  // ... evaluate rules
  return c.json<TriggerResponse>({ status: 'ok' });
});

3.2 Redis

vibe-mod 사용공식 지원한계
get / set / del (rules:active)
zAdd / zRange / zRemRangeByScore (audit)BYSCORE LIMIT count 기본 1000/call
hSet / hGet / hGetAll (rules:meta)
incrBy (counters:compile)
expire (rollback TTL 30일)
multi / exec / watch (atomic audit write)max 20 concurrent tx, 5s exec timeout
전체 cmd/s40,000원본 plan은 1,000으로 추정 → 실제 40배 관대
스토리지500 MB / installation정확
max request size5 MB

3.3 Scheduler

vibe-mod 사용공식 지원한계 (실제)
daily audit retention (cron)cron: "0 3 * * *"최대 10 live recurring actions / install
one-shot dry-run replayscheduler.runJob({runAt: new Date(...)})60 runJob()/min, 60 deliveries/min
seconds-level (<= 1 min)✅ "Faster scheduler" — cron: "*/30 * * * * *"experimental
cancelJob / listJobs
ℹ️ 30-day rollback은 scheduler 대신 Redis TTL 사용 scheduler는 install당 10 recurring 한도라 매 액션마다 새 job 등록 불가능. 대신 redis.set(rollback:<actionId>, ...).expire(2_592_000)로 30일 TTL 부여. 만료되면 자동 삭제. (원본 plan과 동일 결론)

3.4 Settings (정확히 vibe-mod 시나리오 그대로)

공식 settings-and-secrets.mdx에 "OpenAI API Key with isSecret: true" 예시가 그대로 등장. vibe-mod의 LLM 통합 패턴이 정식 권장 패턴임을 의미.

3.5 HTTP Fetch

요구사항공식 답
외부 API 호출 가능?permissions.http.domains 선언 시
HTTPS only
MethodsGET, POST, PUT, DELETE, OPTIONS, PATCH
Timeout30s
Global allowlist에 OpenAIapi.openai.com
Global allowlist에 Anthropic❌ + 정책상 거부
도메인 신청 후 승인 시간"reviewed separately and can add time"
Terms+Privacy Policy필수 (앱 등록 폼에 업로드)

4. 패턴 전환: @devvit/public-api → @devvit/web

4.1 변경 요약

요소원본 plan (구버전)실제 공식 (현재)
패키지@devvit/public-api@devvit/web/server + @devvit/web/shared
설정 파일devvit.yamldevvit.json
트리거 등록Devvit.addTrigger('PostSubmit', (event,ctx)=>{...})devvit.json"triggers":{"onPostSubmit":"/internal/on-post-submit"} + HTTP route
커스텀 포스트Devvit.addCustomPostType({render})devvit.json + Devvit Blocks (interactive-posts) — 더 복잡
메뉴 액션Devvit.addMenuItem({onPress})devvit.json"menu":{"items":[{"endpoint":"/internal/menu/..."}]}
설정Devvit.addSettings()devvit.json"settings":{"global":{...},"subreddit":{...}}
스케줄러 job 등록Devvit.addSchedulerJob()devvit.json"scheduler":{"tasks":{...}} + endpoint
HTTP 서버 프레임워크없음 (closure)Hono 또는 Express (선택)
Reddit API 클라이언트context.redditimport {reddit} from '@devvit/reddit' 또는 '@devvit/web/server'
Reddit 액션 패턴context.reddit.removePost(id)const c = await reddit.getCommentById(id); await c.remove();

4.2 신 패턴의 장점

  • HTTP routes = 표준 웹 패턴, 디버깅 쉬움
  • devvit.json이 single source of truth
  • Hono/Express 선택 자유
  • Validation endpoint 같은 declarative 패턴 지원

5. 공식 Mod Tool 패턴 (quickstart-mod-tool.md verbatim)

5.1 공식 권장 구조: Menu Action + Form

quickstart의 Comment Mop 예시가 정확히 vibe-mod의 base pattern입니다.

// devvit.json
{
  "menu": {
    "items": [
      {
        "label": "Compose rule",
        "description": "Write a moderation rule in English",
        "forUserType": "moderator",
        "location": "subreddit",
        "endpoint": "/internal/menu/compose-rule"
      }
    ]
  },
  "forms": {
    "ruleComposerForm": "/internal/form/compose-rule-submit"
  },
  "permissions": {
    "reddit": { "enable": true, "scope": "moderator" },
    "redis": true,
    "http": { "enable": true, "domains": ["api.openai.com"] }
  },
  "triggers": {
    "onPostSubmit": "/internal/on-post-submit",
    "onAppInstall": "/internal/on-app-install"
  },
  "scheduler": {
    "tasks": {
      "audit-retention": {
        "endpoint": "/internal/scheduler/audit-retention",
        "cron": "0 3 * * *"
      },
      "dry-run-replay": {
        "endpoint": "/internal/scheduler/dry-run-replay"
      }
    }
  },
  "settings": {
    "global": {
      "openaiApiKey": { "type": "string", "label": "OpenAI Key", "isSecret": true }
    },
    "subreddit": {
      "dryRunOnly": { "type": "boolean", "label": "Dry-run mode (no actions)", "defaultValue": true }
    }
  }
}

5.2 코드 샘플 — vibe-mod 핵심 부분 (실현 가능 입증)

// server/index.ts
import { Hono } from 'hono';
import type {
  MenuItemRequest, UiResponse,
  OnPostSubmitRequest, TriggerResponse,
  SettingsValidationRequest
} from '@devvit/web/shared';
import { reddit, settings, scheduler } from '@devvit/web/server';
import { redis } from '@devvit/redis';

const app = new Hono();

// 1. Mod opens "Compose rule" menu → form
app.post('/internal/menu/compose-rule', async (c) => {
  return c.json<UiResponse>({
    showForm: {
      name: 'ruleComposerForm',
      form: {
        title: 'Write a moderation rule',
        acceptLabel: 'Compile + Preview',
        fields: [
          { name: 'rule', label: 'Your rule (English)', type: 'paragraph', defaultValue: '' },
          { name: 'shadow', label: 'Shadow mode (recommended)', type: 'boolean', defaultValue: true }
        ]
      }
    }
  });
});

// 2. Form submit → call OpenAI → store as draft
app.post('/internal/form/compose-rule-submit', async (c) => {
  const { rule, shadow } = await c.req.json<{rule:string,shadow:boolean}>();
  const apiKey = await settings.get('openaiApiKey');

  const llmResp = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
    body: JSON.stringify({
      model: 'gpt-4o-mini',
      response_format: { type: 'json_object' },
      messages: [
        { role: 'system', content: VIBE_MOD_SYSTEM_PROMPT },  // ~600 tok, schema-defining
        { role: 'user', content: rule }
      ],
      max_tokens: 700, temperature: 0.1
    })
  });
  const { choices } = await llmResp.json();
  const compiledRule = JSON.parse(choices[0].message.content);

  // Validate against Zod schema (action whitelist, predicate schema)
  const validated = RuleSchema.parse(compiledRule);

  // Store as draft
  await redis.set('rules:draft', JSON.stringify(validated));

  // Schedule dry-run replay
  await scheduler.runJob({
    name: 'dry-run-replay',
    runAt: new Date(),
    data: { ruleId: validated.id }
  });

  return c.json<UiResponse>({
    showToast: { text: 'Rule compiled. Dry-run running…', appearance: 'success' }
  });
});

// 3. Trigger: PostSubmit → evaluate active rules → take action
app.post('/internal/on-post-submit', async (c) => {
  const { post, author } = await c.req.json<OnPostSubmitRequest>();

  const activeRulesJson = await redis.get('rules:active');
  if (!activeRulesJson) return c.json<TriggerResponse>({ status: 'ok' });

  const activeRules = JSON.parse(activeRulesJson);
  const factBag = await buildFactBag(post, author);  // account age, karma, etc.

  for (const rule of activeRules.rules) {
    if (!rule.enabled) continue;
    if (evaluatePredicate(rule.when, factBag)) {  // pure deterministic
      for (const action of rule.then) {
        await executeAction(action, post, author);
        await writeAuditEntry(rule.id, action, post.id);
      }
    }
  }
  return c.json<TriggerResponse>({ status: 'ok' });
});

// 4. Helper: deterministic action executor
async function executeAction(action: Action, post: any, author: any) {
  const allowed = ['remove', 'lock', 'flair', 'modmail', 'report'];
  if (!allowed.includes(action.action)) throw new Error('forbidden action');

  switch (action.action) {
    case 'remove': {
      const p = await reddit.getPostById(post.id);
      await p.remove();
      break;
    }
    case 'lock': {
      const p = await reddit.getPostById(post.id);
      await p.lock();
      break;
    }
    case 'modmail': {
      await reddit.modMail.create({
        subredditName: post.subreddit,
        subject: action.params.subject,
        body: action.params.body
      });
      break;
    }
    // ... etc
  }
}

5.3 검증된 사실: 무료 호스팅

"Notice that you don't need to worry about running costs for your mod tool, because Reddit hosts all Devvit applications for free." — quickstart-mod-tool.md

→ 인프라 비용 = $0. LLM API 비용만 부담 (OpenAI에 직접 결제).

6. HTTP Fetch + AI 정책 verbatim

6.1 Global allowlist (verbatim)

다음 도메인은 모든 앱에서 즉시 호출 가능 (단, permissions.http.domains에 여전히 명시해야 함):

example.com, site.api.espn.com, cdn.espn.com, discord.com,
api.polygon.io, api.massive.com, polygon.io, slack.com,
lichess.org, api.telegram.org, commentanalyzer.googleapis.com,
language.googleapis.com, statsapi.mlb.com,
api.openai.com,                        ← ✅ vibe-mod 사용
api.scryfall.com, api.nasa.gov,
api.sportradar.us, api.sportradar.com, random.org,
generativelanguage.googleapis.com,    ← ✅ Gemini 대안
youtube.googleapis.com, api.weather.gov, wikipedia.org,
finance.yahoo.com, api.twitter.com, api.petfinder.com,
fonts.googleapis.com, nytimes.com, npr.org, propublica.org,
pbs.org, i.giphy.com, chessboardjs.com

6.2 AI providers 정책 (verbatim, 강조)

### AI providers

At this time, the only AI providers we allow are OpenAI and Google Gemini:

- api.openai.com
- generativelanguage.googleapis.com

Requests to use any other AI provider will be denied. — docs/capabilities/server/http-fetch-policy.md

6.3 도메인 신청 카테고리

  1. API 제공 서비스 (e.g. api.openai.com) — public docs + valid use case면 승인 가능
  2. 제한적 클라우드 (supabase.com, firebase.com, s3.amazonaws.com 등) — 예외 승인 가능, 단 정책 준수
  3. 개인 도메인거부됨. Devvit 서버로 해결 불가능한 use case만 예외

7. Redis 실제 한계

제약실제 값 (공식)원본 plan차이
Max commands per second40,0001,00040× 관대
Max request size5 MB5 MB정확
Max storage500 MB500 MB정확
Pipelining지원 안 함
Setssorted sets만 지원
Key listing지원 안 함 (KEYS *)
Lua scripts지원 안 함
Transactions최대 20 concurrent, 5s exec timeout
ℹ️ Compression (실험적) redisCompressed 클라이언트로 큰 룰 번들(rules:active JSON) 자동 압축 가능. v0.2에서 검토.

8. Scheduler 실제 한계

제약실제 값vibe-mod 영향
Max live recurring actions10 / installationcron 작업은 audit-retention 1개만 사용 → 9 여유
Creation rate (runJob)60/mindry-run 1회 = 1 job, 충분
Delivery rate60/min
Min cron 간격1 min (또는 sec-granular experimental)
Single job timeout30s (HTTP fetch limit과 동일)dry-run replay는 chunking + daisy-chain 필요
⚠️ 30s job timeout 영향 Dry-run replay가 last 24h 100+ 포스트를 평가할 때 30s 안에 못 끝낼 수 있음. 공식 example의 "Migration via daisy-chained scheduler jobs" 패턴을 그대로 차용 가능 (Redis 압축 예시에 자세한 코드 있음).

9. Reddit API 메서드 검증 (RedditAPIClient verbatim)

모든 vibe-mod 필요 메서드가 공식 API에 존재 확인. (docs/api/redditapi/RedditAPIClient/classes/RedditAPIClient.md)

vibe-mod 사용공식 메서드호출 패턴
Post 제거post.remove() (객체 메서드)const p = await reddit.getPostById(id); await p.remove();
Comment 제거comment.remove()quickstart에 정확히 이 패턴 사용
Post/Comment 승인reddit.approve()thing ID로 호출
Lockpost.lock() / comment.lock()객체 메서드
ModMail 생성reddit.modMailModMailService 객체
PM 발송reddit.sendPrivateMessage(options)
유저 조회reddit.getUserByUsername(name)account age 계산용
Ban (whitelist 외)reddit.banUser(options)2-step confirmation 필요
Mutereddit.muteUser(options)
Mod notereddit.addModNote(options)
Removal notereddit.addRemovalNote(options)
Removal reasonreddit.addSubredditRemovalReason()
User flair 설정reddit.createUserFlairTemplateflair template create
모드큐 조회reddit.getModQueue()dry-run preview용
최근 포스트 조회reddit.getNewPosts()dry-run preview용
모드 로그 조회reddit.getModerationLog()v0.2 "learn from mods"
ℹ️ 인증은 Devvit이 자동 처리 "Unlike traditional Reddit API usage, you don't need to create an app at reddit.com/prefs/apps or manage API keys. Devvit handles authentication automatically when you enable the reddit permission in your app." — 우리는 OAuth 토큰 rotation 등에 신경 쓸 필요 없음.

10. Day-0 CLI Smoke Test 결과

10.1 환경 검증

요소결과요구사항
Node.jsv24.15.0 (사용자 머신)22.2.0+ ✅
npx11.12.1
@devvit/cli 최신v0.12.22 (npm)
CLI 실행 가능npx @devvit/cli/0.12.22 darwin-arm64 정상 응답

10.2 실제 devvit new 시도 제약

"Go to https://developers.reddit.com/new and choose Mod Tool under Other templates. Go through the wizard. You will need to create a Reddit account and connect it to Reddit developers. Follow the instructions on your terminal." — quickstart-mod-tool.md

Devvit new는 Reddit 계정 OAuth 로그인 + 브라우저 wizard가 필수. AI 환경에서 비대화형 실행 불가. 사용자 본인이 실행해야 함.

10.3 Day-0 권장 액션 (사용자 단독 수행)

# 1) 브라우저에서 https://developers.reddit.com/new 열고 "Mod Tool" 템플릿 선택
#    → wizard가 끝나면 터미널에 token + dir 생성 안내

# 2) 생성된 디렉토리로 이동
cd vibe-mod  # or whatever you named it

# 3) devvit.json 확인 — http 권한 추가
#    permissions: { http: { enable: true, domains: ['api.openai.com'] }, reddit: {...}, redis: true }

# 4) server/index.ts에 fetch test endpoint 한 줄 추가
#    app.post('/internal/menu/test-openai', ...) — OpenAI ping

# 5) npm run dev — playtest sub에서 실행

# 6) 메뉴 액션 클릭 → 토스트로 OpenAI 응답 표시
#    → 성공 시 vibe-mod 본격 개발 시작
✅ 핵심 결정 사항은 이미 확정됨 정책상 OpenAI/Gemini만 가능하다는 사실이 명확하므로, Day-0의 본질적 의문 ("LLM에 fetch 가능한가?")은 이미 해결. api.openai.com은 globally allowlisted. 사용자의 CLI 실행은 코드 작동 확인이지 정책 게이트 검증이 아님.

11. 추가 제출 요건 (원본 plan 누락)

11.1 Terms and Conditions + Privacy Policy 의무

"Any app that uses fetch must upload Terms and Conditions and a Privacy Policy. Links to each of these documents must be saved in the app details form." — http-fetch.mdx

새 작업 항목: 2 문서 작성 + 호스팅 (GitHub Pages 또는 우리 갤러리 repo 활용 가능):

  • /tos.html — vibe-mod 사용 약관 (1-page)
  • /privacy.html — Privacy Policy (모드의 NL → OpenAI 전송, Reddit 콘텐츠 미전송 명시)

11.2 README "Fetch Domains" 섹션

"If your app uses fetch domains, you must add context to your app's README for the approval process: Create a 'Fetch Domains' section in your README, List each domain you're requesting and explain why you need it." — http-fetch-policy.md

→ README 추가 섹션 (vibe-mod 예시):

## Fetch Domains

The following domains are requested for this app:

- `api.openai.com` — Used to translate moderators' natural-language
  rule descriptions into a structured rule JSON. The LLM call happens
  ONLY at rule-edit time, NOT on every post evaluation. Reddit user
  content (post bodies, usernames) is NEVER sent to the LLM. Only the
  moderator's own typed sentence is sent.

11.3 App publishing 결정

옵션명령적합도
Unlisted (default)npx devvit publish해커톤 심사용으로 충분. 다른 모드가 install 링크로 접근 가능
Public (App Directory)npx devvit publish --publicModerator's Choice 영향력 + 사후 광범위 install

전략: 마감 4-5일 전 (5/22~23) Public publish 신청. Review 1주 걸린다고 명시되어 있음. 늦으면 unlisted로 fallback.

11.4 vibe-mod 제출 패키지 업데이트

  • ☐ App listing 링크 (developers.reddit.com/apps/vibe-mod)
  • NEW: ToS HTML URL
  • NEW: Privacy Policy HTML URL
  • NEW: README의 "Fetch Domains" 섹션
  • ☐ 참가자 Reddit username 전원
  • ☐ Tool overview
  • ☐ Project impact (1-3 communities)
  • ☐ 200-member 미만 sub 데모 포스트 (심사 기간까지 free access)
  • ☐ 60초 데모 비디오 (YouTube/Vimeo 공개)

12. 업데이트된 4일 MVP scope (검증 후)

Day 1 (5/12 화)

  • 사용자 액션: 브라우저로 developers.reddit.com/new → Mod Tool 템플릿 선택 → wizard → terminal에 vibe-mod 디렉토리 생성
  • devvit.json 확장: permissions(reddit moderator, redis, http openai), settings(global openaiApiKey isSecret, subreddit dryRunOnly), menu, forms, triggers(onPostSubmit, onCommentSubmit, onAppInstall), scheduler(audit-retention cron, dry-run-replay one-shot)
  • OpenAI key 발급 + 일일 $5 ceiling + npx devvit settings set openaiApiKey
  • Hono 서버 stub: menu/compose, form/submit, trigger/post-submit endpoints (모두 200 응답만)
  • Redis schema 검증: rules:active, audit ZSET
  • EXIT GATE: npm run dev → 플레이테스트 sub에서 "Compose rule" 메뉴 보이고 form 열림

Day 2 (5/13 수)

  • OpenAI gpt-4o-mini 호출 wrapper + JSON 모드 + Zod schema validator
  • System prompt + 3 few-shot 예제 (rule schema 정의)
  • Action whitelist 하드코딩 (remove/lock/flair/modmail/report; ban/permaban/mute는 confirmation)
  • Predicate evaluator (pure TS, {all|any|not} tree)
  • onPostSubmit handler에서 rules:active 평가 + 첫 action 실행 (reddit.getPostById().remove())
  • EXIT GATE: form에 "Remove posts containing 'discord.gg' from accounts under 7 days" 입력 → compile → 실제 매칭 포스트 제거 + audit 엔트리

Day 3 (5/14 목)

  • Rollback 메커니즘: audit:<actionId> ZSET + reverse-action params + redis.expire(rollback:..., 2592000)
  • Dry-run replay scheduler one-shot: reddit.getNewPosts({limit:100}) 가져와 rules:draft 평가, 매치 카운트를 Redis에 저장
  • Live Log 보기: 별도 메뉴 → zRange(audit, 0, 50) → form display
  • Activate 흐름: shadow mode 24h cron job 등록
  • EXIT GATE: "47 posts would fire" 확인 → Activate (shadow) → 새 포스트 들어옴 → action 실행 → Undo

Day 4 (5/15 금)

  • 5 starter rules onAppInstall에서 seed
  • 50 compiles/day rate limit (Redis counter)
  • 30-day audit retention (cron 0 3 * * *)
  • Error 카피 8개 (LLM down, invalid rule, conflict, permission denied, ...)
  • ToS + Privacy Policy HTML 작성 + 갤러리 repo에 호스팅
  • README "Fetch Domains" 섹션 작성
  • EXIT GATE: 완전한 end-to-end 흐름 + 200-member 미만 sub 생성 + 첫 데모 영상 trial

Days 5-15: Polish + Beta + 데모 영상 (원본 plan 유지)

마감일 작업 (5/27 수)

  • 아침: npx devvit publish (unlisted) — 이미 5/22-23경 Public publish 신청 시도 OK
  • 제출 양식 채우기 + ToS/PrivacyPolicy URL 입력
  • 10:00 PT (KST 5/28 02:00): 제출 완료 목표 (8시간 buffer)

13. 출처 (verbatim 인용 소스)

공식 문서 (cloned from reddit/devvit-docs @ HEAD)

  • docs/quickstart/quickstart-mod-tool.md — Mod Tool 공식 quickstart (Comment Mop 예시)
  • docs/capabilities/server/http-fetch.mdx — fetch 활성화 + global allowlist
  • docs/capabilities/server/http-fetch-policy.md — AI provider 정책 + 도메인 신청 카테고리
  • docs/capabilities/server/triggers.mdx — 모든 트리거 이벤트 + 등록 패턴
  • docs/capabilities/server/redis.mdx — Redis 명령어 + 한계 + transactions
  • docs/capabilities/server/scheduler.mdx — 스케줄러 + cron + one-shot + daisy chain
  • docs/capabilities/server/settings-and-secrets.mdx — 설정 + isSecret + OpenAI 예시
  • docs/capabilities/server/reddit-api.mdx — Reddit API 개요 + 인증 자동 처리
  • docs/api/redditapi/RedditAPIClient/classes/RedditAPIClient.md — 100+ 메서드 전체 API surface
  • docs/capabilities/client/menu-actions.mdx — menu items + forUserType moderator + 10분 보안 제약
  • docs/guides/faq.mdx — publish/install 패턴, unlisted vs public

관련 자료 (이 갤러리 repo 내)


검증 완료: 2026-05-11 KST · 다음 단계: 사용자가 Day-1 CLI 액션 수행 시점에 본 보고서를 implementation guide로 사용

↑ 맨 위로