Skip to content

Security: opspresso/agent-studio

Security

docs/SECURITY.md

๋ณด์•ˆ

์ธ์ฆยท์ธ๊ฐ€, ์‹œํฌ๋ฆฟ, ๋„คํŠธ์›Œํฌ, ๋ชจ๋ธ ์ž…๋ ฅ๊ณผ ์ €์žฅ ๋ฐ์ดํ„ฐ์˜ ๋ณด์•ˆ ๊ฒฝ๊ณ„๋ฅผ ์„ค๋ช…ํ•œ๋‹ค. ์„ค์ •๋งŒ์œผ๋กœ ๋ณด์žฅํ•˜๋Š” ๊ฒƒ๊ณผ ๋ฐฐํฌ ํ™˜๊ฒฝ์ด ์ฑ…์ž„์ง€๋Š” ๊ฒƒ์„ ๊ตฌ๋ถ„ํ•œ๋‹ค.

๊ด€๋ จ ๋ฌธ์„œ: ์—ฌ๊ธฐ์„œ ์–ธ๊ธ‰ํ•˜๋Š” ๋ณ€์ˆ˜๋Š” CONFIGURATION.md, ์—”๋“œํฌ์ธํŠธ๋ณ„ ์ธ๊ฐ€๋Š” API.md, ์กฐ๊ฐ๋“ค์ด ์–ด๋–ป๊ฒŒ ๋งž๋ฌผ๋ฆฌ๋Š”์ง€๋Š” ARCHITECTURE.md.

์ธ์ฆ

Better Auth์˜ advanced.cookiePrefix๋Š” agent-studio๋‹ค. ๊ธฐ๋ณธ ์„ธ์…˜ ์ฟ ํ‚ค๋Š” agent-studio.session_token์ด๋ฉฐ HTTPS ์„ค์ •์—์„œ๋Š” __Secure-๊ฐ€ ๋ถ™๋Š”๋‹ค. ํŽ˜์ด์ง€ ๊ฒŒ์ดํŠธ๋„ ๊ฐ™์€ ์ ‘๋‘์–ด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. Agent Memory์˜ agent-memory ์ฟ ํ‚ค์™€ ๋ถ„๋ฆฌํ•˜๋ฉฐ ์ด์ „ better-auth ์ฟ ํ‚ค๋Š” ์ฝ์ง€ ์•Š๋Š”๋‹ค. ์ ‘๋‘์–ด ๋ณ€๊ฒฝ์„ ๋ฐฐํฌํ•˜๋ฉด ๊ธฐ์กด ๋ธŒ๋ผ์šฐ์ € ์„ธ์…˜์€ ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•ด์•ผ ํ•œ๋‹ค.

๋ฉ€ํ‹ฐ ๋„๋ฉ”์ธ ๋กœ๊ทธ์ธ์€ BETTER_AUTH_ALLOWED_HOSTS์˜ ์ •ํ™•ํ•œ ํ˜ธ์ŠคํŠธ๋งŒ ํ—ˆ์šฉํ•œ๋‹ค. config.authBaseUrl์ด BETTER_AUTH_URL์˜ ํ”„๋กœํ† ์ฝœ๊ณผ ํ—ˆ์šฉ ๋ชฉ๋ก์„ Better Auth์— ์ „๋‹ฌํ•˜๊ณ , ์š”์ฒญ์˜ ๋ณด์กด๋œ Host(์—†์œผ๋ฉด request URL)๋กœ ์ฝœ๋ฐฑ์„ ๊ฒฐ์ •ํ•œ๋‹ค. ์ „๋‹ฌ๋œ ํ˜ธ์ŠคํŠธยทํ”„๋กœํ† ์ฝœ ํ—ค๋”๋Š” ์‹ ๋ขฐํ•˜์ง€ ์•Š๋Š”๋‹ค. ์„ธ์…˜ยทOAuth state ์ฟ ํ‚ค๋Š” host-only์ด๋ฉฐ HTTPS์ด๋ฉด Secure๋‹ค. ๋กœ๊ทธ์ธ ์‹คํŒจ์˜ ์ƒ๋Œ€ Location: /login?error=๋„ ์ฝœ๋ฐฑ ๋„๋ฉ”์ธ์„ ์œ ์ง€ํ•œ๋‹ค. ์„ค์ •ยทprovider ๋“ฑ๋ก์€ ์„ค์น˜ ์ ˆ์ฐจ๋ฅผ ๋”ฐ๋ฅธ๋‹ค.

Better Auth 1.7์€ ์•ฑ์˜ ์ปค๋„ฅ์…˜ ํ’€ ์œ„์—์„œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž์‹ ์˜ Postgres ์–ด๋Œ‘ํ„ฐ๋กœ ๋ˆ๋‹ค. user, session, account, verification ์€ ๊ทธ๊ฒƒ์ด ์†Œ์œ ํ•˜๋Š” ํ…Œ์ด๋ธ”์ด๊ณ (migrations.ts ๊ฐ€ ๋งŒ๋“ ๋‹ค), emailยทtoken ์˜ ์œ ์ผ์„ฑ์€ ํ…Œ์ด๋ธ”์˜ ์œ ๋‹ˆํฌ ์ œ์•ฝ์ด๋‹ค.

๊ณ„์ •์˜ ์‹๋ณ„์ž๋Š” providerId + accountId์ด๋ฉฐ ์ด ์กฐํ•ฉ์— unique index๋ฅผ ๋‘”๋‹ค. issuer๋Š” ๊ธฐ์กด ๊ฐ’์„ ๋ณด์กดํ•˜๋Š” nullable ์ด๋ ฅ ์ปฌ๋Ÿผ์ด๊ณ  ์ƒˆ ๊ณ„์ •์—๋Š” ์“ฐ์ง€ ์•Š๋Š”๋‹ค. ๊ฐ™์€ ๊ณ„์ • ํ‚ค์˜ ์ค‘๋ณต์€ unique index๊ฐ€ ๊ฑฐ๋ถ€ํ•œ๋‹ค. ์„œ๋กœ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๋ฅผ ์ž๋™ ๋ณ‘ํ•ฉํ•˜๊ฑฐ๋‚˜ provider๋ฅผ ์ž„์˜๋กœ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค.

๋กœ๊ทธ์ธ ์ˆ˜๋‹จ์€ ์ „๋ถ€ ์„ ํƒ์ด๊ณ  ์„ค์น˜๊ฐ€ ๊ณ ๋ฅธ๋‹ค (src/lib/config.ts ์˜ authProviders, ๊ทธ๋Œ€๋กœ auth.ts ์™€ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ):

์ˆ˜๋‹จ ์ผœ๋Š” ๊ฒƒ ์„ฑ์งˆ
Keycloak KEYCLOAK_ISSUER + KEYCLOAK_CLIENT_ID + KEYCLOAK_CLIENT_SECRET Better Auth์˜ Keycloak helper์™€ genericOAuth๋กœ discovery๋ฅผ ์ฝ๊ณ  PKCE(S256)๋ฅผ ์“ด๋‹ค. ์ฝœ๋ฐฑ /api/auth/callback/keycloak
ํ‘œ์ค€ OIDC OIDC_ISSUER + OIDC_CLIENT_ID + OIDC_CLIENT_SECRET genericOAuth๋กœ discovery ๋ฌธ์„œ์—์„œ ์ฐพ๊ณ  PKCE๋ฅผ ์“ด๋‹ค. ์ฝœ๋ฐฑ /api/auth/callback/oidc
Google GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET ์ฝœ๋ฐฑ /api/auth/callback/google
์ด๋ฉ”์ผ + ๋น„๋ฐ€๋ฒˆํ˜ธ AUTH_PASSWORD=true ๊ฐ€์ž… ํผ์ด ์—†๋‹ค(disableSignUp): ์‹ ์› ์ œ๊ณต์ž๊ฐ€ ๋ณด์ฆํ•˜๋Š” ์‚ฌ๋žŒ์€ ์ฒซ ๋กœ๊ทธ์ธ์œผ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ๋˜์ง€๋งŒ, ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ณ„์ •์€ ์•„๋ฌด๋„ ๋ณด์ฆํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ๊ด€๋ฆฌ์ž(BOOTSTRAP_ADMIN_EMAIL/PASSWORD, ๋ถ€ํŒ… ๋•Œ ํ•œ ๋ฒˆ)์™€ ๊ด€๋ฆฌ์ž๊ฐ€ ์˜๋„์ ์œผ๋กœ ๋งŒ๋“  ๊ณ„์ •๋ฟ์ด๋‹ค. ์ฒซ ๊ด€๋ฆฌ์ž์™€ ์ œ๊ณต์ž๊ฐ€ ์ฃฝ์—ˆ์„ ๋•Œ์˜ ๋น„์ƒ ์ ‘๊ทผ์šฉ์ด๋‹ค

Keycloakยทํ‘œ์ค€ OIDCยทGoogle์„ ํ•จ๊ป˜ ์„ค์ •ํ•˜๋ฉด ๊ฐ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ํ‘œ์‹œํ•œ๋‹ค. Keycloak์€ discovery์˜ issuerยทJWKS๊ฐ€ ์žˆ์–ด์•ผ ๋“ฑ๋กํ•˜๋ฉฐ ID ํ† ํฐ์˜ ์„œ๋ช…ยทissuerยทaudience์™€ ์š”์ฒญ๋ณ„ nonce๋ฅผ Better Auth๊ฐ€ ๊ฒ€์ฆํ•œ๋‹ค. ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์€ ์„ค์ •์˜ ์กด์žฌ๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฏ€๋กœ discovery ์‹คํŒจ ์‹œ์—๋„ ํ‘œ์‹œ๋˜์ง€๋งŒ ์ธ์ฆ์€ ์‹คํŒจํ•œ๋‹ค. ์ œ๊ณต์ž ๋ณต๊ตฌ ๋’ค ์•ฑ์„ ์žฌ์‹œ์ž‘ํ•ด discovery๋ฅผ ๋‹ค์‹œ ์ฝ๋Š”๋‹ค. Keycloak issuer๋Š” ๋ฐฐํฌ ์šด์˜์ž๊ฐ€ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์ง€์ •ํ•˜๋ฉฐ ์‚ฌ๋‚ด ์ฃผ์†Œ๋ฅผ ํ—ˆ์šฉํ•œ๋‹ค. ๋ธŒ๋ผ์šฐ์ €์™€ ์„œ๋ฒ„ ๋ชจ๋‘ ๊ทธ ์ฃผ์†Œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค. Keycloak role์„ ์•ฑ tier๋กœ ๋งคํ•‘ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ธฐ์กด ๊ด€๋ฆฌ์ž ์„ค์ •๊ณผ ๋ฉค๋ฒ„ tier ์ •์ฑ…์„ ์ ์šฉํ•œ๋‹ค. Keycloak์œผ๋กœ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž์˜ ์•ฑ ๋กœ๊ทธ์•„์›ƒ์€ ์•ฑ ์„ธ์…˜๋งŒ ์ข…๋ฃŒํ•˜๋ฉฐ Keycloak SSO ์„ธ์…˜์€ ์œ ์ง€ํ•œ๋‹ค. GoogleยทOIDCยทKeycloak ๋กœ๊ทธ์ธ์€ ๊ธฐ์กด ๊ณ„์ •์—๋„ ์ œ๊ณต์ž์˜ ์ตœ์‹  ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ฐ˜์˜ํ•ด ์ด๋ฆ„๊ณผ ํ”„๋กœํ•„ ์ด๋ฏธ์ง€๋ฅผ ๊ฐฑ์‹ ํ•œ๋‹ค. ์ œ๊ณต์ž๊ฐ€ ์‚ฌ์ง„์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์œผ๋ฉด ์ €์žฅ๋œ ์ด๋ฏธ์ง€๋ฅผ ์œ ์ง€ํ•œ๋‹ค. ํ”„๋กœํ•„ ๋™๊ธฐํ™”๋Š” ChatยทAgent ์†Œ์œ ๊ถŒ์— ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ์กด ์ด๋ฉ”์ผ์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ œ๊ณต์ž๊ฐ€ ์ƒˆ ์ด๋ฉ”์ผ์„ ๋ณด๋‚ด๋ฉด ํ—ˆ์šฉ ๋„๋ฉ”์ธ์€ ๋‹ค์‹œ ๊ฒ€์‚ฌํ•˜์ง€๋งŒ, ์†Œ์œ ๊ถŒ ์ด๋ฉ”์ผ๊ณผ ํ•ด๋‹น ์ด๋ฉ”์ผ์˜ ์ธ์ฆ ์ƒํƒœ๋Š” ๋ณด์กดํ•œ๋‹ค. ์•ฑ์˜ tier๋Š” ์ œ๊ณต์ž ํ”„๋กœํ•„์—์„œ ๊ฐ€์ ธ์˜ค์ง€ ์•Š์œผ๋ฉฐ ๊ธฐ์กด ๊ด€๋ฆฌ์žยท๋“ฑ๊ธ‰ ์ •์ฑ…์„ ๋”ฐ๋ฅธ๋‹ค.

STAGE=alpha|prod๋Š” ๋กœ๊ทธ์ธ ์ˆ˜๋‹จ์ด ํ•˜๋‚˜๋„ ์—†์œผ๋ฉด ๋ถ€ํŒ…์„ ๊ฑฐ๋ถ€ํ•œ๋‹ค(assertAccessControlConfig); local ์€ scripts/dev-session.ts ๊ฐ€ ์„ธ์…˜์„ ๋งŒ๋“ค์–ด ์ฃผ๋ฏ€๋กœ ์—†์–ด๋„ ๋œ๋‹ค. ๋กœ๊ทธ์ธ์€ ์ˆ˜๋‹จ๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ ALLOWED_EMAIL_DOMAINS ๋กœ ์ œํ•œ๋œ๋‹ค. ์‚ฌ์šฉ์ž ์ƒ์„ฑ๊ณผ ์„ธ์…˜ ์ƒ์„ฑ ์–‘์ชฝ์˜ ํ›…์—์„œ, ๊ทธ๋ž˜์„œ ์ด๋ฏธ ์žˆ๋Š” ์‚ฌ์šฉ์ž๋„ ๋„๋ฉ”์ธ์ด ๋ชฉ๋ก์—์„œ ๋น ์ง€๋ฉด ๋‹ค์Œ ๋กœ๊ทธ์ธ์— ๊ฑฐ์ ˆ๋œ๋‹ค. ๋นˆ ๋ชฉ๋ก์€ ๋ชจ๋“  ๋„๋ฉ”์ธ์„ ํ—ˆ์šฉํ•œ๋‹ค. STAGE=alpha|prod ๋„ ๊ทธ ์ƒํƒœ๋กœ ์ •์ƒ ๋ถ€ํŒ…ํ•œ๋‹ค (๋ถ€ํŒ… ์‹œ ๊ฒ€์ฆ ์ฐธ๊ณ ).

admin ์ „์šฉ ๋ฉค๋ฒ„ ๋ชฉ๋ก์€ Better Auth ์˜ user ํ–‰์„ ์ฝ๋Š”๋‹ค. createdAt ์€ ๊ฐ€์ž… ์‹œ๊ฐ์ด๊ณ , lastLoginAt ์€ ์„ธ์…˜ ์ƒ์„ฑ์— ์„ฑ๊ณตํ•œ ๋’ค ๊ฐฑ์‹ ๋œ๋‹ค. ๊ทธ ์ด์ „๋ถ€ํ„ฐ ์žˆ๋˜ ์‚ฌ์šฉ์ž๋Š” ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•˜๊ธฐ ์ „๊นŒ์ง€ ๋งˆ์ง€๋ง‰ ๋กœ๊ทธ์ธ ๊ฐ’์ด ์—†๋‹ค. ํƒ€์ž„์Šคํƒฌํ”„ ๊ฐฑ์‹  ์‹คํŒจ๋Š” ๋กœ๊ทธ์— ๋‚จ์ง€๋งŒ, ์‹ ์› ์ œ๊ณต์ž ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•œ ๊ฒƒ์„ ์ธ์ฆ ์‹คํŒจ๋กœ ๋ฐ”๊พธ์ง€๋Š” ์•Š๋Š”๋‹ค.

๋‘ ๊ฐœ์˜ ๊ฒŒ์ดํŠธ, ์˜๋„์ ์œผ๋กœ

ํ‘œ๋ฉด ๊ฒŒ์ดํŠธ ๋ฌด์—‡์„ ๊ฒฐ์ •ํ•˜๋Š”๊ฐ€
ํŽ˜์ด์ง€ src/proxy.ts ๋กœ๊ทธ์•„์›ƒ ์ƒํƒœ์˜ ๋ฐฉ๋ฌธ์ž๋ฅผ /login?next=โ€ฆ ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
API ๋ผ์šฐํŠธ withAuth / withMemberAuth / withAdminAuth (src/lib/session.ts) ์„ธ์…˜์ด ์—†์œผ๋ฉด 401. member gate๋Š” tier๋ฅผ, admin gate๋Š” ์•„๋ž˜์˜ effective admin ํŒ์ •์„ ์‚ฌ์šฉํ•œ๋‹ค. ์‹คํŒจ๋Š” 403์ด๋ฉฐ ์„ฑ๊ณตํ•˜๋ฉด SessionUser๋ฅผ ๊ฑด๋„จ๋‹ค

src/shared/pageAccess.ts ๋Š” ์–ด๋–ค ํŽ˜์ด์ง€๊ฐ€ ๊ณต๊ฐœ์ธ์ง€์— ๋Œ€ํ•œ ๋‹จ์ผ ์†Œ์œ ์ž๋‹ค. /, /login, /guide. ๊ฐ€์ด๋“œ๋Š” ๋กœ๊ทธ์ธ ์—†์ด ์ฝ๋Š” ์ •์  ์•ˆ๋‚ด์ด๋ฉฐ, ๊ฐ€์ด๋“œ์—์„œ ์—ฐ๊ฒฐํ•˜๋Š” Agentยท์„ค์ • ํŽ˜์ด์ง€์™€ API๋Š” ๊ฐ์ž์˜ ์ธ์ฆยท๊ถŒํ•œ ๊ฒ€์‚ฌ๋ฅผ ์œ ์ง€ํ•œ๋‹ค. src/proxy.ts์™€ ๋ธŒ๋ผ์šฐ์ €์˜ ๋งŒ๋ฃŒ ์„ธ์…˜ redirect๊ฐ€ ๊ฐ™์€ ํŒ์ •์„ ์ฝ๋Š”๋‹ค. matcher ๊ฐ€ ๋‹ฟ๋Š” ๋‚˜๋จธ์ง€ ์ „๋ถ€๋Š” ์„ธ์…˜์„ ์š”๊ตฌํ•˜๋ฏ€๋กœ ์ƒˆ ๋ผ์šฐํŠธ๋Š” ๊ธฐ๋ณธ์ด ๋ณดํ˜ธ ์ƒํƒœ ๋‹ค. ๊ทธ ๋ฐฉํ–ฅ์€ ์˜๋„์ ์ด๋‹ค. ๊ณต๊ฐœ ํŽ˜์ด์ง€๋ฅผ ๋ชฉ๋ก์— ๋„ฃ๋Š” ๊ฒƒ์„ ์žŠ์œผ๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ 1๋ถ„ ์•ˆ์— ์‹ ๊ณ ํ•˜๋Š” ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๊ฐ€ ์ƒ๊ธฐ์ง€๋งŒ, ๋น„๊ณต๊ฐœ ํŽ˜์ด์ง€๋ฅผ ๋„ฃ๋Š” ๊ฒƒ์„ ์žŠ์œผ๋ฉด ์กฐ์šฉํžˆ ์‹คํŒจํ•œ๋‹ค.

ํŽ˜์ด์ง€ ๊ฒŒ์ดํŠธ๋Š” ์„ธ์…˜ ์ฟ ํ‚ค์˜ ์œ ํšจ์„ฑ์ด ์•„๋‹ˆ๋ผ ์กด์žฌ ๋ฅผ ํ™•์ธํ•œ๋‹ค. ์œ ํšจ์„ฑ๊นŒ์ง€ ํ™•์ธํ•˜๋ ค๋ฉด ๋ชจ๋“  ๋‚ด๋น„๊ฒŒ์ด์…˜๋งˆ๋‹ค ์„ธ์…˜์„ ์ฝ์–ด์•ผ ํ•˜๊ณ , ๊ทธ๋Ÿฌ๊ณ ๋„ ๊ทธ๊ฒƒ์€ ์ธ๊ฐ€ ๊ฒฐ์ •์ด ์•„๋‹ˆ๋‹ค. ์ธ๊ฐ€๋Š” ์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ์ง€๋Š” ์š”์ฒญ์„ ๋ณด๋Š” withAuth ์™€ assertAgentOwner ์—์„œ ์„œ๋ฒ„ ์ธก์— ๋‚จ๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ์กด์žฌํ•˜์ง€๋งŒ ์œ ํšจํ•˜์ง€ ์•Š์€ ์ฟ ํ‚ค๋Š” ํŽ˜์ด์ง€์— ๋„๋‹ฌํ•˜๊ณ  ๊ทธ ๋’ค์˜ API ์—์„œ 401 ์„ ๋ฐ›๋Š”๋‹ค. ๋ธŒ๋ผ์šฐ์ €์˜ ๊ณตํ†ต ์‘๋‹ต ๊ฒฝ๊ณ„๋Š” ๊ฐ™์€ origin์˜ /api/* 401์„ ๋ฐ›์œผ๋ฉด ํ˜„์žฌ pathยทqueryยทfragment๋ฅผ next๋กœ ๋ณด์กดํ•ด /login์œผ๋กœ full navigationํ•œ๋‹ค. Root layout์ด ์ด๋ฏธ ์„ธ์…˜์„ ์œ ํšจํ•˜์ง€ ์•Š๋‹ค๊ณ  ํŒ์ •ํ•œ ๋ณดํ˜ธ ํŽ˜์ด์ง€๋„ ๊ฐ™์€ ๊ฒฝ๋กœ๋ฅผ ํƒ„๋‹ค. ๊ฒŒ์ดํŠธ๊ฐ€ ์—†์• ๋Š” ๊ฒƒ์€ ํ‰๋ฒ”ํ•œ ๋กœ๊ทธ์•„์›ƒ ์ƒํƒœ์ด๋ฉฐ, ๊ทธ ๋ฐฉ๋ฌธ์ž์—๊ฒŒ ์ฝ˜์†” shell์ด๋‚˜ API error box๋ฅผ ๋ Œ๋”๋งํ•˜์ง€ ์•Š๋Š”๋‹ค.

/api ๋Š” matcher ๋ฐ–์— ์žˆ๋‹ค. ๊ทธ ๋ผ์šฐํŠธ๋“ค์€ ์Šค์Šค๋กœ ์ธ์ฆํ•˜๋ฉฐ, ํ”„๋กœ๊ทธ๋žจ ํ˜ธ์ถœ์ž์—๊ฒŒ๋Š” HTML ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ฐ˜๋“œ์‹œ 401 ๋กœ ๋‹ตํ•ด์•ผ ํ•œ๋‹ค.

next ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ์ฃผ์†Œ์ฐฝ์—์„œ ์˜ค๋ฏ€๋กœ /login ์€ ๊ทธ๊ฒƒ์„ safeNextPath(src/shared/safeNextPath.ts)๋กœ ๋˜์ฝ๋Š”๋‹ค. //host ์™€ /\host ๋ฅผ ๊ฑฐ๋ถ€ํ•˜์ง€ ์•Š์œผ๋ฉด ๋กœ๊ทธ์ธ ํ”Œ๋กœ์šฐ๊ฐ€ ์˜คํ”ˆ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๊ฐ€ ๋œ๋‹ค.

๋กœ๊ทธ์ธ ์˜ค๋ฅ˜๋Š” signInError.ts์˜ ์ฝ”๋“œ๋กœ ์ „๋‹ฌํ•˜๊ณ  ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๊ฐ€ ์ž๊ธฐ ๋ฌธ๊ตฌ๋กœ ํ‘œ์‹œํ•œ๋‹ค. ์•Œ ์ˆ˜ ์—†๋Š” error ๊ฐ’์€ ์ผ๋ฐ˜ ์˜ค๋ฅ˜๋กœ ๋ฐ”๊พธ๋ฉฐ ์ œ๊ณต์ž์˜ ํ…์ŠคํŠธ๋‚˜ ํ—ˆ์šฉ ๋„๋ฉ”์ธ ๋ชฉ๋ก์„ ๊ทธ๋Œ€๋กœ ๋ฐ˜์‚ฌํ•˜์ง€ ์•Š๋Š”๋‹ค.

์ธ๊ฐ€ ๋ชจ๋ธ

Agent ๋Š” ๊ณต์œ  ์นดํƒˆ๋กœ๊ทธ์ด๋˜, ๊ณต๊ฐœ ๋ฒ”์œ„(visibility)๋ฅผ ๊ฐ–๋Š”๋‹ค. public(๊ธฐ๋ณธ๊ฐ’์ด์ž visibility ํ•„๋“œ๊ฐ€ ์—†์„ ๋•Œ์˜ ์˜๋ฏธ)์€ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ์กฐํšŒํ•˜๊ณ  member ์ด์ƒ์ด ์‹คํ–‰ยท๋ณต์ œํ•œ๋‹ค. private์€ ์†Œ์œ ์ž๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ด€๋ฆฌ์ž๋„ ์‹คํ–‰ยท๋ณต์ œ ๊ถŒํ•œ์„ ์–ป์ง€ ์•Š๋Š”๋‹ค. ์„ค์ • ๊ด€๋ฆฌ ๊ถŒํ•œ์€ ๋ณ„๋„ ๊ฒ€์‚ฌํ•œ๋‹ค. ๊ณต๊ฐœ ๋ฒ”์œ„ ํŒ์ •์€ src/domain/agent/access.ts์˜ mayAccessAgent๊ฐ€ ์†Œ์œ ํ•˜๊ณ , Agent ์กฐํšŒ๋ฅผ ํฌํ•จํ•œ ๊ฒ€์‚ฌ๋Š” assertAgentAccessible์„ ์‚ฌ์šฉํ•œ๋‹ค.

์ฝ˜์†”ยทChat๊ณผ ๊ฐœ์ธ API ํ† ํฐ์€ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž์˜ ํ˜„์žฌ assertAgentAccessible ํŒ์ •์„ ์ ์šฉํ•œ๋‹ค. ๊ฐœ์ธ ํ† ํฐ์€ Agent ์†Œ์œ ์ž๊ฐ€ ์•„๋‹ˆ๋ผ ๋ฐœ๊ธ‰ ์‚ฌ์šฉ์ž ID์— ๋ฌถ์ธ๋‹ค. SlackยทTelegramยทTeams๋Š” ์ „๋‹ฌ ์ธ์ฆ ํ›„ ์—ฐ๊ฒฐํ•œ Studio ์‚ฌ์šฉ์ž์˜ ํ˜„์žฌ ๊ณ„์ •ยทmember ๋“ฑ๊ธ‰ยท Agent ์ ‘๊ทผ์„ ํ™•์ธํ•œ๋‹ค. ์ด๋ฉ”์ผ์ด๋‚˜ bot ์†Œ์œ ์ž๋งŒ์œผ๋กœ ํ˜ธ์ถœ์ž๋ฅผ ์ถ”์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค. Slack ์•ฑ์˜ ์ฑ„๋„ ํ‚ค์›Œ๋“œ ์•Œ๋ฆผ์€ ํ‚ค์›Œ๋“œ๋ฅผ ์ €์žฅํ•œ ์ธ์ฆ ๊ณ„์ •์˜ ์‚ฌ์šฉ์ž ID์™€ revision์œผ๋กœ ์‹คํ–‰ํ•œ๋‹ค. ํ˜„์žฌ ๊ณ„์ •ยทAgent ์ ‘๊ทผยท์—ฐ๋™ ํ™œ์„ฑํ™”ยท๋“ฑ๋ก revision์„ ๋ชจ๋ธยท๋„๊ตฌยทWorkspace ํšจ๊ณผ ์ „์— ๋‹ค์‹œ ๊ฒ€์‚ฌํ•˜๋ฉฐ, ํ‚ค์›Œ๋“œ ๋ณ€๊ฒฝยท์‚ญ์ œ๋กœ ๊ถŒํ•œ์„ ์ฒ ํšŒํ•œ๋‹ค. ๋‹ค๋ฅธ ์•ฑ์˜ DMยทthread ๋‹ต๊ธ€๊ณผ ๋ด‡ ์ž์‹ ์˜ ๋ฉ”์‹œ์ง€๋Š” ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. Slack์˜ ๋ช…๋ นยทnative ์ค‘๋‹จยทthread-start๋Š” ์‚ฌ๋žŒ์˜ ์—ฐ๊ฒฐ ๊ฒ€์‚ฌ๋ฅผ ์ง€๋‚œ๋‹ค. TriggerยทWebhook์˜ ์ „๋‹ฌ ์ธ์ฆ์€ ๋จธ์‹  ์š”์ฒญ ์ธ์ฆ์„ ๋”ฐ๋ฅธ๋‹ค.

private agent ๋ฅผ local subagent ๋กœ ๋ฐ”์ธ๋”ฉ ํ•˜๋Š” ๊ฒƒ๋„ ์ฝ๊ธฐ๋‹ค: ํŽธ์ง‘์ž๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋Š” agent ๋Š” Agent ์„ค์ • ์ €์žฅ ์‹œ์ ์— ๊ฑฐ์ ˆ๋œ๋‹ค (assertSubavailableAgentsAccessible). ์ด๋ฏธ ๋ฐ”์ธ๋”ฉ๋œ ์ฐธ์กฐ๋Š” agent ๊ฐ€ ๋’ค๋Šฆ๊ฒŒ private ์ด ๋˜์–ด๋„ ํŽธ์ง‘ ๊ฐ€๋Šฅ์„ฑ์„ ์žƒ์ง€ ์•Š๋Š”๋‹ค. ์‹คํ–‰ ์‹œ์ ์˜ transfer ๋Š” ํ˜„์žฌ ํ˜ธ์ถœ์ž์˜ Agent ์ ‘๊ทผ ๊ถŒํ•œ์„ ๋‹ค์‹œ ํ™•์ธํ•œ๋‹ค. ๋น„์šฉ ๋Œ€์‹œ๋ณด๋“œ์˜ agent ํ•ฉ๊ณ„ ๋Š” visibility ์ด์ „์ฒ˜๋Ÿผ ์—ด๋ ค ์žˆ๋‹ค: ์ด๋ฆ„๊ณผ ์ง€์ถœ ์ง‘๊ณ„๋Š” ์นดํƒˆ๋กœ๊ทธ ์šด์˜์˜ ์ผ๋ถ€๋กœ ๋‚จ๊ฒจ ๋‘” ๊ฒฐ์ •์ด๋‹ค.

๋ฆฌ์†Œ์Šค ์ฝ๊ธฐ ์“ฐ๊ธฐ
Agent, Agent ์„ค์ • ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž (assertAgentAccessible, public ์€ ์ „์›, private ์€ ์†Œ์œ ์ž) ์†Œ์œ ์ž (assertAgentOwner)
Agent trace ์†Œ์œ ์ž โ€”
Agent Slack ์„ค์ • ์†Œ์œ ์ž ์†Œ์œ ์ž
๊ฐœ์ธ Agent API token ๋ฐœ๊ธ‰ ์‚ฌ์šฉ์ž ๋ณธ์ธ๊ณผ ํ˜„์žฌ Agent ์ ‘๊ทผ ๋ฐœ๊ธ‰ ์‚ฌ์šฉ์ž ๋ณธ์ธ; ์ƒ์„ฑยทreveal์€ ํ˜„์žฌ token ์‚ฌ์šฉ tier๋„ ๊ฒ€์‚ฌ
trigger ์†Œ์œ ์ž ์†Œ์œ ์ž
๊ฐœ์ธ MCP ์—ฐ๊ฒฐ member ์ด์ƒ์ธ ๋ณธ์ธ๊ณผ ํ˜„์žฌ Agent ์ ‘๊ทผ ๋™์ผ ์‚ฌ์šฉ์ž๋งŒ ์—ฐ๊ฒฐยทํ•ด์ œ; binding ์ดˆ์•ˆ ํ—ค๋” ๊ฒ€์‚ฌ๋Š” Agent ์†Œ์œ ์ž
ํ˜ธ์ถœ์ž๋ณ„ usage (usage/actors) ์†Œ์œ ์ž โ€”
Agent usage ํ•ฉ๊ณ„ ๋กœ๊ทธ์ธํ•œ ๋ชจ๋“  ์‚ฌ์šฉ์ž โ€”
Skill / MCP ์„œ๋ฒ„ / plugin guest๋ฅผ ํฌํ•จํ•œ ๋กœ๊ทธ์ธ ์‚ฌ์šฉ์ž (withAuth) admin (withAdminAuth)
์•ฑ ์„ค์ • admin admin
๋ชจ๋ธ ์ฆ๊ฒจ์ฐพ๊ธฐ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž ๋ณธ์ธ member ์ด์ƒ์ธ ์‚ฌ์šฉ์ž ๋ณธ์ธ
๋ฉค๋ฒ„ ๋””๋ ‰ํ„ฐ๋ฆฌ admin admin (tier ๋ณ€๊ฒฝ, member.set-tier ๋กœ ๊ฐ์‚ฌ)
Chat ์†Œ์œ ์ž๋งŒ (์†Œ์œ ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด 404) ์†Œ์œ ์ž๋งŒ
Workspace ์†Œ์œ ์ž์™€ ํ˜„์žฌ Agent ์ ‘๊ทผ ๊ฒ€์‚ฌ ์†Œ์œ ์ž; ์‹คํ–‰ยทGit ์Šน์ธ์€ ํ™œ์„ฑํ™”ยท์ •์ฑ…ยท์Šน์ธ ์ƒํƒœ๋„ ๊ฒ€์‚ฌ
Audio ๊ณต์œ  ์ฒ˜๋ฆฌ ์„ค์ • ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ member Agent ์†Œ์œ ์ž
์˜ค๋””์˜ค jobยท๋น„๊ณต๊ฐœ source ํŒŒ์ผ ์ž‘์—…/ํŒŒ์ผ ์†Œ์œ ์ž์™€ Agent ์ ‘๊ทผ ๊ฒ€์‚ฌ ์†Œ์œ ์ž ๋ฒ”์œ„์™€ job/file ์ƒํƒœ์— ๋”ฐ๋ฅธ ์กฐ์ž‘
์ผ๋ฐ˜ Artifact ์ƒ์„ฑยท์ฒจ๋ถ€ ์†Œ์œ ์ž ๋˜๋Š” Agent ์†Œ์œ ์ž member ์ด์ƒ์ด๋ฉฐ ๊ฐ™์€ ์†Œ์œ ๊ถŒ ๋ฒ”์œ„์—์„œ ์‚ญ์ œ. ๋น„๊ณต๊ฐœ source ํŒŒ์ผ์€ ์œ„ ์ „์šฉ ๊ฒฝ๊ณ„

trace ์™€ Slack ์„ค์ •์€ ์ฝ๊ธฐ ๋„ ๊ฒŒ์ดํŠธ๋˜๋Š”๋ฐ, ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ๋Ÿฐํƒ€์ž„ ์ž…์ถœ๋ ฅ๊ณผ ๋งˆ์Šคํ‚น๋œ ์ž๊ฒฉ ์ฆ๋ช…์˜ ๊ฐ€์žฅ์ž๋ฆฌ๋ฅผ ๋…ธ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. agent ํ•ฉ๊ณ„ ๋Š” ์นดํƒˆ๋กœ๊ทธ๊ฐ€ ๊ณต์œ ๋˜๋ฏ€๋กœ ์—ด์–ด ๋‘”๋‹ค. ํ˜ธ์ถœ์ž๋ณ„ ๋‚ด์—ญ์€ ๊ฐœ์ธ์„ ์ง€๋ชฉํ•˜๋ฏ€๋กœ usage/actors ๋Š” ๊ทธ๋ ‡์ง€ ์•Š๋‹ค. capability ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋Š” guest๋„ member์™€ ๊ฐ™์€ ๋ฉ”๋‰ด์—์„œ ์กฐํšŒํ•œ๋‹ค. guest๋Š” Agentยท์—ฐ๋™ยท๋ ˆ์ง€์ŠคํŠธ๋ฆฌยท๋ชจ๋ธ ์ฆ๊ฒจ์ฐพ๊ธฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ Artifact๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์—†์œผ๋ฉฐ, ๊ฐ•๋“ฑ๋œ ๊ธฐ์กด Agent ์†Œ์œ ์ž์—๊ฒŒ๋„ ๊ฐ™์€ ์ œํ•œ์ด ์ ์šฉ๋œ๋‹ค. Agent ๋ณ€๊ฒฝ API๋Š” withMemberAuth์™€ ์†Œ์œ ๊ถŒ ๊ฒ€์‚ฌ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•œ๋‹ค. ์ดˆ์•ˆ preview์™€ MCP ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ๋Š” ์™ธ๋ถ€ ์„œ๋น„์Šค ์š”์ฒญ์„ ์‹คํ–‰ํ•˜๋ฏ€๋กœ member ์ด์ƒ์„ ์š”๊ตฌํ•œ๋‹ค. Agent ์‹คํ–‰ยทChat ์ „์†กยท์Šน์ธ ์žฌ๊ฐœยทWorkspace ์ƒˆ ์ž‘์—…์€ member ์ด์ƒ์—๊ฒŒ ํ—ˆ์šฉํ•œ๋‹ค. guest๋Š” ๊ธฐ์กด ๋ณธ์ธ ๊ธฐ๋ก์„ ์กฐํšŒํ•˜๊ณ  ์‹คํ–‰ ์ค‘๋‹จยท์ข…๋ฃŒยท์Šน์ธ ํ๊ธฐ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์‹คํ–‰ ์ค‘ ๊ฐ•๋“ฑ๋„ ์ƒˆ ๋ชจ๋ธยท๋„๊ตฌ ํšจ๊ณผ๋ฅผ ์ฐจ๋‹จํ•œ๋‹ค.

isAdminEmail vs isConfiguredAdmin

์„ค์น˜ ๊ณต์šฉ ์ž์›์˜ ๊ด€๋ฆฌ์ž ๊ถŒํ•œ๊ณผ Agent ์†Œ์œ ๊ถŒ์€ ๋ณ„๊ฐœ๋‹ค. isAdminEmail์€ ๊ณต์šฉ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌยท์„ค์ •์˜ ๊ด€๋ฆฌ์ž ๋ชฉ๋ก์„ ํ™•์ธํ•˜๋ฉฐ, ๋ชฉ๋ก์ด ๋น„๋ฉด member ์ด์ƒ์—๊ฒŒ ์ดˆ๊ธฐ ๊ด€๋ฆฌ ๊ถŒํ•œ์„ ํ—ˆ์šฉํ•œ๋‹ค. isEffectiveAdmin์€ ์ด ๊ทœ์น™๊ณผ ์ €์žฅ๋œ admin tier๋ฅผ ํ•ฉ์นœ๋‹ค. isConfiguredAdmin์€ ADMIN_EMAILS์— ๋ช…์‹œ๋œ ๊ณ„์ •์„ ์ฐพ์•„ admin ์Šน๊ฒฉ๊ณผ tier ์ž ๊ธˆ์— ์‚ฌ์šฉํ•œ๋‹ค. ์–ด๋А ๊ด€๋ฆฌ์ž ํŒ์ •๋„ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ Agent ๊ด€๋ฆฌ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜์ง€ ์•Š๋Š”๋‹ค.

GET /api/me๋Š” email, tier, ๊ณต์šฉ ์ž์› ๊ด€๋ฆฌ์šฉ isAdmin์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. Agent ํŽธ์ง‘ ์—ฌ๋ถ€๋Š” isAgentOwner์™€ tierMayEdit๋กœ ํŒ๋‹จํ•œ๋‹ค. ์„œ๋ฒ„์˜ assertAgentOwner๊ฐ€ ๊ฐ™์€ ์†Œ์œ ๊ถŒ์„ ๊ฒ€์‚ฌํ•œ๋‹ค.

ADMIN_EMAILS์— ์ง€์ •๋œ ์ฃผ์†Œ๋Š” ๋กœ๊ทธ์ธ ๋˜๋Š” ๋ฉค๋ฒ„/ํ”„๋กœํ•„ ์กฐํšŒ ๋•Œ admin tier๋กœ ์Šน๊ฒฉ๋˜๊ณ , ๋ชฉ๋ก์— ์žˆ๋Š” ๋™์•ˆ tier๊ฐ€ ์ž ๊ธด๋‹ค. ๋ชฉ๋ก์—์„œ ์ œ๊ฑฐํ•ด๋„ ์ž๋™ ๊ฐ•๋“ฑํ•˜์ง€ ์•Š์œผ๋ฉฐ ๊ด€๋ฆฌ์ž๊ฐ€ ๋“ฑ๊ธ‰์„ ๋ณ€๊ฒฝํ•œ๋‹ค. tier๋Š” Better Auth user ํ–‰์˜ ์„œ๋ฒ„ ์ „์šฉ ํ•„๋“œ๋‹ค. ์‹คํ–‰์€ ์‚ฌ์šฉ์ž ID๋กœ ํ˜„์žฌ ๊ณ„์ •ยท๋“ฑ๊ธ‰ยทAgent ์ ‘๊ทผ์„ ํ™•์ธํ•œ๋‹ค. ์ด๋ฉ”์ผ ๊ธฐ๋ฐ˜ tier ์กฐํšŒ์˜ ์บ์‹œ๋Š” 30์ดˆ์ด๋ฉฐ ๋ณ€๊ฒฝ์„ ์ฒ˜๋ฆฌํ•œ ์ธ์Šคํ„ด์Šค์—์„œ ๋ฌดํšจํ™”๋œ๋‹ค.

์›” ๋น„์šฉ ํ•œ๋„๋Š” Settings โ†’ Access์˜ memberTiers์™€ memberTierLimits๊ฐ€ ์ •ํ•œ๋‹ค. admin์€ ๋ฌด์ œํ•œ์ด๋ฉฐ guest๋Š” ํ•œ๋„์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ์‹คํ–‰ํ•˜์ง€ ๋ชปํ•œ๋‹ค. ๋‚˜๋จธ์ง€ ๋“ฑ๋ก ๋“ฑ๊ธ‰์€ member ๊ถŒํ•œ์„ ๊ฐ–๋Š”๋‹ค. ๋ฏธ๋“ฑ๋ก ๋“ฑ๊ธ‰์€ guest๋กœ ํ•ด์„ํ•œ๋‹ค. ๋“ฑ๊ธ‰ ์‚ญ์ œ์™€ ์‚ฌ์šฉ์ž ๋ฐฐ์ •์€ ๊ณต๋™ transaction lock์„ ์‚ฌ์šฉํ•˜๋ฉฐ ์‚ฌ์šฉ์ž๊ฐ€ ๋‚จ์€ ๋“ฑ๊ธ‰์€ ์‚ญ์ œํ•  ์ˆ˜ ์—†๋‹ค. ์›” ํ•œ๋„๋Š” ์„ค์ • ์บ์‹œ TTL์— ๋”ฐ๋ผ ์ „ํŒŒ๋œ๋‹ค.

์›” ์ƒํ•œ์€ ChatยทWorkspace๋ฅผ ํฌํ•จํ•œ ๋ฉค๋ฒ„ ์ž์‹ ์˜ ์ผ๋ณ„ ํ–‰์„ UTC ์›” 1์ผ๋ถ€ํ„ฐ ํ•ฉ์‚ฐํ•œ๋‹ค. ์‹คํ–‰ ์ „์— ์ด๋ฏธ ํ•œ๋„์— ๋„๋‹ฌํ•˜๋ฉด ์ƒˆ ์‹คํ–‰์„ ๊ฑฐ์ ˆํ•œ๋‹ค. ๋“ฑ๊ธ‰ยท์‚ฌ์šฉ๋Ÿ‰ ์กฐํšŒ ์‹คํŒจ๋„ ์‹คํ–‰์„ ์ค‘๋‹จํ•œ๋‹ค. ์™„๋ฃŒ๋œ ์‚ฌ์šฉ๋Ÿ‰์„ ๊ธฐ์ค€์œผ๋กœ ๊ฒ€์‚ฌํ•˜๋ฏ€๋กœ ์ง„ํ–‰ ์ค‘์ธ ์‹คํ–‰์ด ์ž”์•ก์„ ์ดˆ๊ณผํ•  ์ˆ˜ ์žˆ๋‹ค. ํ”„๋กœํ•„ ํŽ˜์ด์ง€๊ฐ€ ์ฝ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์ฐฝ, ๊ฐ™์€ ํ–‰์ด๋‹ค. ๋ชจ๋“  ํ˜ธ์ถœ ์ถœ์ฒ˜์—๋Š” ํ™•์ธ๋œ Studio ์‚ฌ์šฉ์ž๊ฐ€ ์žˆ๊ณ  ๊ฐ™์€ ์‚ฌ์šฉ์ž ID์˜ ๊ฐœ์ธ ํ•œ๋„๋ฅผ ๊ณต์œ ํ•œ๋‹ค. ํ”Œ๋žซํผ actor๋Š” ์ถœ์ฒ˜ ๊ธฐ๋ก์„ ์œ„ํ•ด ์œ ์ง€ํ•œ๋‹ค. ๊ฐœ์ธ API token์€ ๋ฐœ๊ธ‰ ์‚ฌ์šฉ์ž์˜ ์•ˆ์ •์ ์ธ ID์— ๋ฌถ์ด๋ฉฐ ์ธ์ฆ๋งˆ๋‹ค ํ˜„์žฌ ๊ณ„์ •ยท๋“ฑ๊ธ‰ยทAgent ์ ‘๊ทผ์„ ๊ฒ€์‚ฌํ•œ๋‹ค. ๊ณ„์ • ์กด์žฌ ์—ฌ๋ถ€์™€ ํ˜„์žฌ tier๋Š” ์ด๋ฉ”์ผ tier ์บ์‹œ ๋Œ€์‹  ์‚ฌ์šฉ์ž ID๋กœ ์กฐํšŒํ•œ๋‹ค. ํ™•์ธ๋œ ์‚ฌ์šฉ์ž์˜ ํ˜ธ์ถœ ๊ถŒํ•œ ๊ฑฐ์ ˆ์€ 403, ์ž˜๋ชป๋œ credential์ด๋‚˜ ์‚ญ์ œ๋œ ๋ฐœ๊ธ‰ ๊ณ„์ •์€ 401์ด๋‹ค. ๊ถŒํ•œ ์ €์žฅ์†Œ ์žฅ์• ๋Š” ์ธ์ฆ์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

Agent ์„ค์ •ยท์—ฐ๋™ยทWorkspace ์ •์ฑ…์˜ ๋ณ€๊ฒฝ๊ณผ Agent ๊ด€๋ฆฌ ์ž๋ฃŒยทTraceยทํ˜ธ์ถœ์ž๋ณ„ Usage ์กฐํšŒ๋Š” assertAgentOwner๋กœ ์†Œ์œ ์ž๋ฅผ ํ™•์ธํ•œ๋‹ค. public์€ ๊ณต์œ  ์‹คํ–‰ ๋ฒ”์œ„์ด๋ฉฐ ํŽธ์ง‘ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ผ๋ฐ˜ Artifact๋Š” ์ƒ์„ฑ์ž ๋˜๋Š” ํ•ด๋‹น Agent ์†Œ์œ ์ž๊ฐ€ ๊ด€๋ฆฌํ•˜๋ฉฐ, ๋น„๊ณต๊ฐœ source ํŒŒ์ผ์€ ํŒŒ์ผ ์†Œ์œ ์ž๋งŒ ์ ‘๊ทผํ•œ๋‹ค. ์กฐํšŒ๋Š” ๋ณ€๊ฒฝ ๊ฐ์‚ฌ ํ–‰์„ ์ƒ์„ฑํ•˜์ง€ ์•Š๋Š”๋‹ค.

์ €์žฅ๋œ ์‹œํฌ๋ฆฟ

์ €์žฅ๋˜๋Š” ๋ชจ๋“  ์ž๊ฒฉ ์ฆ๋ช…. MCP ์„œ๋ฒ„ ํ—ค๋”, Agent๋ณ„ ํ—ค๋” ์˜ค๋ฒ„๋ผ์ด๋“œ, MCP OAuth ์˜ access/refresh tokenยทclient secretยท์ธ๊ฐ€ ์ค‘์ธ PKCE verifier, Slack ๋ด‡ token ๊ณผ ์„œ๋ช… ์‹œํฌ๋ฆฟ, Telegram ๋ด‡ token ๊ณผ webhook ์‹œํฌ๋ฆฟ, Teams(Azure Bot) ํด๋ผ์ด์–ธํŠธ ์‹œํฌ๋ฆฟ, ๊ฐœ์ธ APIยทWebhook token, ๊ทธ๋ฆฌ๊ณ  ์‹œํฌ๋ฆฟ์ธ ์•ฑ ์„ค์ •(LLM API ํ‚ค์™€ plugins ์ €์žฅ์†Œ์˜ GitHub token)์€ AES_ENCRYPTION_KEY ๋กœ AES-256-GCM ์•”ํ˜ธํ™”๋œ๋‹ค(src/infrastructure/crypto/secretEncryption.ts). ์ƒˆ ๊ฐ’์€ ๋ชจ๋‘ enc:v2: ๋กœ ์“ด๋‹ค. v2 ๋Š” row ์™€ field ์ •์ฒด์„ฑ์„ AES-GCM AAD ๋กœ ๋ฌถ์œผ๋ฏ€๋กœ ์•”ํ˜ธ๋ฌธ๋งŒ ๋‹ค๋ฅธ ์œ„์น˜๋กœ ์˜ฎ๊ธฐ๋ฉด ์ธ์ฆ์— ์‹คํŒจํ•œ๋‹ค. ๊ธฐ์กด enc:v1: ๊ฐ’์€ ๋‹ค์‹œ ์ €์žฅํ•˜๊ฑฐ๋‚˜ ์žฌ๋ฐœ๊ธ‰ํ•˜๊ธฐ ์ „๊นŒ์ง€ ๊ทธ๋Œ€๋กœ ์ฝ๋Š”๋‹ค.

๊ฐœ์ธ credential์€ agent + purpose + userId + credentialId์— ๋ฌถ์ธ๋‹ค. Slack ์˜ bot tokenยทsigning secret, Telegram ์˜ bot tokenยทwebhook secret, Teams ์˜ app password ๋Š” agent + integration + field ๋ฅผ ์“ด๋‹ค. MCP registry header ๋Š” ํ•ญ๋ชฉ ์ด๋ฆ„๊ณผ header ์ด๋ฆ„์—, managed MCP ์˜ environment ๋Š” ํ•ญ๋ชฉ ์ด๋ฆ„๊ณผ ๋ณ€์ˆ˜ ์ด๋ฆ„์— ๋ฌถ์ธ๋‹ค. HTTP header์˜ override ๋ณ‘ํ•ฉ๋งŒ ์ด๋ฆ„์˜ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๋ฌด์‹œํ•˜๊ณ , AAD ๋Š” environment์™€ ๊ฐ™์€ ๊ณตํ†ต map ๊ทœ์น™์— ๋”ฐ๋ผ ์ €์žฅ๋œ ํ‚ค ์ฒ ์ž๋ฅผ ๊ทธ๋Œ€๋กœ ์“ด๋‹ค.

Agent์˜ MCP header override๋Š” agent + configuration + mcp + server + header์— ๋ฌถ์ธ๋‹ค. ํ˜„์žฌ ์„ค์ •์„ ๊ฐ™์€ Agent์—์„œ ์ˆ˜์ •ํ•ด๋„ ์•”ํ˜ธํ™” ๋ฌธ๋งฅ์€ ์œ ์ง€๋˜๋ฉฐ ๋‹ค๋ฅธ Agent๋กœ ๋ณต์ œํ•  ๋•Œ๋Š” ์‹œํฌ๋ฆฟ์„ ๋ณต์‚ฌํ•˜์ง€ ์•Š๋Š”๋‹ค.

์ฝ์„ ๋•Œ์˜ ๋งˆ์Šคํ‚น

์ฝ๊ธฐ๋Š” ๊ธธ์ด๋ฅผ ๋ณด์กดํ•˜๋Š” ๋งˆ์Šคํฌ ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ฝ˜์†”์ด ๊ฐ’์„ ๋ณด์—ฌ ์ฃผ์ง€ ์•Š์œผ๋ฉด์„œ ์–ด๋–ค ์ž๊ฒฉ ์ฆ๋ช…์ด ์„ค์ •๋ผ ์žˆ๋Š”์ง€๋Š” ๋ณด์—ฌ ์ค„ ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋‹ค:

ํ‰๋ฌธ ๊ธธ์ด ๋“œ๋Ÿฌ๋‚˜๋Š” ๋ถ€๋ถ„
9์ž ๋ฏธ๋งŒ ์—†์Œ (* ร— ๊ธธ์ด)
9์ž ์ด์ƒ ์•ž 4์ž์™€ ๋’ค 4์ž

๋“œ๋Ÿฌ๋‚œ ๋‘ ๊ฐ€์žฅ์ž๋ฆฌ๊ฐ€ ๋งŒ๋‚˜๋Š” ์ผ์€ ๊ฒฐ์ฝ” ํ—ˆ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ฐ€์žฅ์ž๋ฆฌ๋ฅผ ๋“œ๋Ÿฌ๋‚ด๋ ค๋ฉด ํ‰๋ฌธ์ด ํ•„์š”ํ•˜๋ฏ€๋กœ ๋งˆ์Šคํ‚น์€ ๋ณตํ˜ธํ™”๋ฅผ ํ•œ๋‹ค. ๊ทธ๊ฒƒ์„ ํ˜ธ์ถœํ•˜๋Š” admin/์†Œ์œ ์ž ๊ฒŒ์ดํŠธ๊ฐ€ ๊ฑธ๋ฆฐ ์ฝ๊ธฐ ๋ทฐ ์•ˆ์—์„œ๋งŒ์ด๊ณ , ๋ณตํ˜ธํ™” ์‹คํŒจ๋Š” ์—๋Ÿฌ๋ฅผ ๋‚ด๋Š” ๋Œ€์‹  ๊ฐ’์„ ํ†ต์งธ๋กœ ๊ฐ์ถ˜๋‹ค. ์•„๋ฌด๊ฒƒ๋„ ๋“œ๋Ÿฌ๋‚˜์ง€ ์•Š์„ ๋งŒํผ ์งง์€ ๊ฐ’์€ ์•„์˜ˆ ๋ณตํ˜ธํ™”๋˜์ง€ ์•Š์œผ๋ฉฐ, ๊ทธ ํŒ๋‹จ์€ ์•”ํ˜ธ๋ฌธ ๊ธธ์ด๋กœ ๋‚ด๋ฆฐ๋‹ค(AES-GCM ์€ ํ‰๋ฌธ ๊ธธ์ด๋ฅผ ๋ณด์กดํ•œ๋‹ค).

์“ธ ๋•Œ์˜ ๋งˆ์Šคํฌ

์—…๋ฐ์ดํŠธ ์‹œ ๋งˆ์Šคํ‚น๋œ ๊ฐ’์ด๋‚˜ ๋นˆ ๊ฐ’์€ ์ €์žฅ๋œ ์‹œํฌ๋ฆฟ์„ ๋ณด์กดํ•œ๋‹ค. ์ €์žฅ๋œ ์ƒ๋Œ€๊ฐ€ ์—†๋Š” ํ‚ค์— ์˜จ ๋งˆ์Šคํ‚น๋œ ๊ฐ’์€ ๋ฒ„๋ฆฐ๋‹ค. ๋งˆ์Šคํฌ๋Š” ์ด๋ฏธ ์žˆ๋Š” ์‹œํฌ๋ฆฟ์„ ํ™•์ธํ•ด ์ค„ ์ˆ˜๋งŒ ์žˆ๊ณ , ๋งŒ๋“ค์–ด ๋‚ผ ์ˆ˜๋Š” ์—†๋‹ค. ํ—ค๋” ์˜ค๋ฒ„๋ผ์ด๋“œ ๋งต์˜ null ์€ ๋ช…์‹œ์  ์ œ๊ฑฐ๋กœ ๊ทธ๋Œ€๋กœ ํ†ต๊ณผํ•œ๋‹ค. ์ œ๊ฑฐ๋Š” ์‹œํฌ๋ฆฟ์ด ์•„๋‹ˆ๋‹ค. ํ—ค๋” ์ด๋ฆ„์€ ์ผ๋ฐ˜ ๋ฐ์ดํ„ฐ ํ‚ค๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค. API ๊ฒ€์ฆยท์•”ํ˜ธํ™” ๋ณ‘ํ•ฉยทMCP ์ „์†ก์—์„œ __proto__ ๊ฐ™์€ ์ด๋ฆ„๋„ ๋ˆ„๋ฝํ•˜์ง€ ์•Š์œผ๋ฉฐ, SDK๊ฐ€ ๋งŒ๋“  ํ”„๋กœํ† ์ฝœ ํ—ค๋”๋Š” ๋“ฑ๋กํ•œ ๊ธฐ๋ณธ ํ—ค๋”๋ณด๋‹ค ์šฐ์„ ํ•œ๋‹ค. ์ƒˆ๋กœ ์ž…๋ ฅํ•œ ๊ฐ’์€ enc:v1: ๋˜๋Š” enc:v2: ๋กœ ์‹œ์ž‘ํ•˜๋”๋ผ๋„ ํ‰๋ฌธ์œผ๋กœ ์ทจ๊ธ‰ํ•ด ํ•ญ์ƒ ์ƒˆ๋กœ ์•”ํ˜ธํ™”ํ•œ๋‹ค. ์•”ํ˜ธ๋ฌธ ์ ‘๋‘์‚ฌ๋Š” ์ €์žฅ์†Œ์—์„œ ์ฝ์€ ๊ฐ’์˜ ํ˜•์‹์ผ ๋ฟ, API ์ž…๋ ฅ์ด ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์ €์žฅ ๊ฐ’์ด๋ผ๋Š” ์ฆ๊ฑฐ๊ฐ€ ์•„๋‹ˆ๋‹ค. Agent๋ณ„ MCP ๋ฌธ์ž์—ด ์˜ค๋ฒ„๋ผ์ด๋“œ๋Š” ์ €์žฅ ๋‹น์‹œ registry URL ์˜ fingerprint ์™€ ํ•จ๊ป˜ ๋ณด๊ด€ํ•œ๋‹ค. ๊ฐ™์€ ์ด๋ฆ„์˜ URL ์ด ๋ฐ”๋€Œ๊ฑฐ๋‚˜ fingerprint ๊ฐ€ ์—†๋Š” ์˜ˆ์ „ ๊ฐ’์ด๋ฉด ์˜› ์‹œํฌ๋ฆฟ์„ ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค. ์ƒˆ endpoint ์šฉ ์ž๊ฒฉ ์ฆ๋ช…์„ ๋‹ค์‹œ ์ž…๋ ฅํ•ด์•ผ ํ•œ๋‹ค.

reveal ์—”๋“œํฌ์ธํŠธ

์ด ์•ฑ์ด ๋ฐœ๊ธ‰ํ•˜๋Š” ์‹œํฌ๋ฆฟ ์ค‘ ๋‘˜์€ ํ‰๋ฌธ์œผ๋กœ ๋˜์ฝ์„ ์ˆ˜ ์žˆ๋‹ค:

์‹œํฌ๋ฆฟ ์—”๋“œํฌ์ธํŠธ ๋ˆ„๊ฐ€
๊ฐœ์ธ Agent API token POST /api/agents/{name}/token/reveal ๋ฐœ๊ธ‰ ์‚ฌ์šฉ์ž ๋ณธ์ธ
๊ฐœ์ธ Webhook token POST /api/agents/{name}/webhook-token/reveal ๋ฐœ๊ธ‰ ์‚ฌ์šฉ์ž ๋ณธ์ธ

๋‘˜ ๋ชจ๋‘ ์ฝ๊ธฐ์—๋„ POST๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์‘๋‹ต ๋ณธ๋ฌธ์ด ์‚ด์•„ ์žˆ๋Š” ์ž๊ฒฉ ์ฆ๋ช…์ด๋ฏ€๋กœ ์บ์‹œยท๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋กยท ํ”„๋ฆฌํŽ˜์น˜ ๋ฐ”๊นฅ์— ๋‘”๋‹ค. ๋ชจ๋“  reveal์€ ์‹ค์ œ ์ˆ˜ํ–‰์ž์˜ email๊ณผ ํ•จ๊ป˜ ๊ฐ์‚ฌ ํ–‰์„ ๋‚จ๊ธด๋‹ค.

๋”ฐ๋ผ์„œ ๋‘˜์€ ํ•ด์‹œ๊ฐ€ ์•„๋‹ˆ๋ผ ์•”ํ˜ธํ™”ํ•ด์„œ ์ €์žฅ๋˜๋ฉฐ, ์ด๋Š” ์˜๋„๋œ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋‹ค. ๋ฐ์ดํ„ฐ์Šคํ† ์–ด๋งŒ์œผ๋กœ๋Š” ํ•˜๋‚˜๋„ ์“ธ ์ˆ˜ ์—†์ง€๋งŒ, ๋ฐ์ดํ„ฐ์Šคํ† ์–ด ๋”ํ•˜๊ธฐ AES_ENCRYPTION_KEY ๋ฉด ์“ธ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ ํ‚ค๋ฅผ ํ…Œ์ด๋ธ” ๋คํ”„์™€ ์‚ด์•„ ์žˆ๋Š” agent ์ž๊ฒฉ ์ฆ๋ช… ์‚ฌ์ด์— ์„œ ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ๋‹ค๋ค„๋ผ. ๊ณต์šฉ agent token์€ ๊ฐœ์ธ credential๋กœ ๊ฐ„์ฃผํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ƒˆ ์‚ฌ์šฉ์ž๋ณ„ ํ† ํฐ์„ ๋ฐœ๊ธ‰ํ•ด์•ผ ํ•œ๋‹ค.

๋ฐœ๊ธ‰ํ•œ ์‹œํฌ๋ฆฟ์˜ ์ ‘๋‘์‚ฌ

์ด ์•ฑ์ด ๋ฐœ๊ธ‰ํ•˜๋Š” ์‹œํฌ๋ฆฟ์€ GitHub ์˜ ghp_/gho_ ์ฒ˜๋Ÿผ ์ œํ’ˆ๊ณผ ์ข…๋ฅ˜๋ฅผ ๋ฐํžˆ๋Š” ์ ‘๋‘์‚ฌ๋ฅผ ์ง€๋…€(src/shared/generatedSecret.ts), ์œ ์ถœ๋œ ๋ฌธ์ž์—ด์ด ๋ฌด์—‡์„ ์—ฌ๋Š”์ง€ ์ถ”์ ํ•  ์ˆ˜ ์žˆ๋‹ค:

์ ‘๋‘์‚ฌ ์‹œํฌ๋ฆฟ
ast_ ๊ฐœ์ธ Agent API token (๋ฐœ๊ธ‰ ์‚ฌ์šฉ์ž ๊ด€๋ฆฌ)
asw_ ๊ฐœ์ธ Webhook token (๋ฐœ๊ธ‰ ์‚ฌ์šฉ์ž ๊ด€๋ฆฌ)
asl_ ๋ฉ”์‹ ์ € ์‚ฌ์šฉ์ž ์—ฐ๊ฒฐ์šฉ ์ผํšŒ์šฉ ์ฝ”๋“œ (10๋ถ„, ํ•ด์‹œ๋งŒ ์ €์žฅ)
asg_ Telegram webhook ์‹œํฌ๋ฆฟ (agent ๋งˆ๋‹ค ๋ฐœํ–‰. Telegram ์—๊ฒŒ๋งŒ ๊ฑด๋„ค๊ณ  ๊ฒฐ์ฝ” reveal ํ•˜์ง€ ์•Š๋Š”๋‹ค)

๋žœ๋ค secret์€ 32๋ฐ”์ดํŠธ(256๋น„ํŠธ)๋‹ค. ๊ฐœ์ธ APIยทWebhook token์€ ๊ณต๊ฐœ credential UUID์™€ secret์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉฐ UUID๋งŒ์œผ๋กœ๋Š” ์ธ์ฆ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ณผ๊ฑฐ ๊ณต์šฉ ํ† ํฐ์€ ๊ฐœ์ธ ์‚ฌ์šฉ์ž๋ฅผ ์ถ”์ •ํ•˜๋Š” ๊ทผ๊ฑฐ๋กœ ์“ฐ์ง€ ์•Š๋Š”๋‹ค.

agent token ์˜ ํ‘œ์‹œ์šฉ ๋งˆ์Šคํฌ๋Š” ์ƒ์„ฑ ์‹œ์ ์— ๊ณ„์‚ฐ๋ผ ์•”ํ˜ธ๋ฌธ ์˜†์— ์ €์žฅ๋˜๋ฏ€๋กœ, token ์„ ๋‚˜์—ดํ•˜๋Š” ๋ฐ๋Š” ๋ณตํ˜ธํ™” ๋น„์šฉ์ด ๋“ค์ง€ ์•Š๋Š”๋‹ค. ๊ทธ ๋งˆ์Šคํฌ๋Š” ์ ‘๋‘์‚ฌ์™€ ๊ฐ€์žฅ์ž๋ฆฌ ๋ฌธ์ž๋งŒ ์‹ค์–ด ๋‚˜๋ฅด๋ฉฐ, token ์„ ๋ณต์›ํ•˜๊ธฐ์— ์ถฉ๋ถ„ํ•œ ์ ์€ ๊ฒฐ์ฝ” ์—†๋‹ค.

๋จธ์‹  ํ˜ธ์ถœ์ž์˜ ์š”์ฒญ ์ธ์ฆ

์„ธ์…˜ ์ฟ ํ‚ค๊ฐ€ ์—†๋Š” ํ˜ธ์ถœ์ž๋Š” ํ‘œ๋ฉด๋ณ„ ์ž๊ฒฉ ์ฆ๋ช…์„ ์‚ฌ์šฉํ•œ๋‹ค:

ํ‘œ๋ฉด ์ž๊ฒฉ ์ฆ๋ช… ๊ฒ€์ฆ
์‹คํ–‰ ์—”๋“œํฌ์ธํŠธ (predict, chat/completions, agent) Authorization: Bearer ast_โ€ฆ ๊ฐœ์ธ credential์˜ ์ƒ์ˆ˜ ์‹œ๊ฐ„ ๋น„๊ต, ํ˜„์žฌ ๋ฐœ๊ธ‰ ๊ณ„์ •ยทtierยทAgent ์ ‘๊ทผ ๊ฒ€์‚ฌ. ๋ฐœ๊ธ‰ ์‚ฌ์šฉ์ž๋กœ์„œ ์‹คํ–‰๋œ๋‹ค (authenticateExecution)
Slack ์ด๋ฒคํŠธ Slack ์„œ๋ช… ์‹œํฌ๋ฆฟ HMAC + timingSafeEqualString, 5๋ถ„ ๋ฆฌํ”Œ๋ ˆ์ด ์œˆ๋„, agent ๋ณ„ ์‹œํฌ๋ฆฟ
Telegram webhook X-Telegram-Bot-Api-Secret-Token ์ด ํ”Œ๋žซํผ์ด webhook ์„ ๋“ฑ๋กํ•  ๋•Œ ์“ด agent ๋ณ„ ์‹œํฌ๋ฆฟ(asg_โ€ฆ)๊ณผ timingSafeEqualString ๋น„๊ต. Telegram ์ด ๋ฐฐ๋‹ฌ๋งˆ๋‹ค ๊ทธ๋Œ€๋กœ ๋˜๋Œ๋ ค์ฃผ๋ฉฐ, ๊ทธ ๋ฐ–์— ํ™•์ธํ•  ์„œ๋ช…์€ ์—†๋‹ค
Teams messaging endpoint Bot Framework bearer ํ† ํฐ (JWT) RS256 ์„œ๋ช…์„ ์„œ๋น„์Šค๊ฐ€ ๊ณต๊ฐœํ•œ JWKS(login.botframework.com) ๋กœ ๊ฒ€์ฆํ•˜๊ณ , ๋ฐœ๊ธ‰์ž https://api.botframework.com, audience = ๊ทธ ๋ด‡์˜ App ID, exp/nbf(5๋ถ„ skew), ๊ทธ๋ฆฌ๊ณ  serviceurl ํด๋ ˆ์ž„ = activity ์˜ serviceUrl ์„ ์š”๊ตฌํ•œ๋‹ค. ๋‹ต์€ ๊ทธ ์ฃผ์†Œ๋กœ ์ด ์•ฑ์˜ ํ† ํฐ์„ ๋ถ™์—ฌ ๋‚˜๊ฐ€๋ฏ€๋กœ. Emulator ํ† ํฐ์€ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค (src/infrastructure/teams/client.ts)
Webhook trigger X-Trigger-Secret ๋˜๋Š” GitHub X-Hub-Signature-256 ๊ฐœ์ธ Webhook ํ† ํฐ์˜ ์ƒ์ˆ˜ ์‹œ๊ฐ„ ๋น„๊ต ๋˜๋Š” ๊ณต๊ฐœ credential ์‹๋ณ„์ž๋กœ ์„ ํƒํ•œ ํ† ํฐ์˜ HMAC-SHA256 ๊ฒ€์ฆ. ๋ฐœ๊ธ‰ ์‚ฌ์šฉ์ž ID๋กœ ํ˜„์žฌ ๊ณ„์ •ยทAgent ์ ‘๊ทผ์„ ํ™•์ธํ•œ๋‹ค. GitHub ํ—ค๋”๊ฐ€ ์žˆ์œผ๋ฉด ์„œ๋ช… ๊ฒ€์ฆ์„ ๊ฐ•์ œํ•˜๊ณ  ์ผ๋ฐ˜ ์‹œํฌ๋ฆฟ์œผ๋กœ ํด๋ฐฑํ•˜์ง€ ์•Š๋Š”๋‹ค. ์„œ๋ช…๋œ ping์€ ์‹คํ–‰ํ•˜์ง€ ์•Š์œผ๋ฉฐ GitHub delivery ID๋กœ ์ค‘๋ณต์„ ์ฐจ๋‹จํ•œ๋‹ค
Workspace GitHub webhook X-Hub-Signature-256๊ณผ X-GitHub-Delivery ๋ณ„๋„ ๋ฐฐํฌ ์‹œํฌ๋ฆฟ์œผ๋กœ ๊ฒ€์ฆํ•˜๊ณ  PR/CI ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋งŒ ๊ฐฑ์‹ ํ•œ๋‹ค. Git ์‹คํ–‰ ์Šน์ธ์ด ์•„๋‹ˆ๋‹ค
CronJob ํ‹ฑ. schedule ์Šค์บ”(/api/triggers/scan), ์นดํƒˆ๋กœ๊ทธ ์žฌ์ƒ‰์ธ(/api/catalog/reindex), plugins sync(/api/plugins/sync/scan) X-Scan-Token SCHEDULE_SCAN_TOKEN ๊ณผ timingSafeEqualString ๋น„๊ต. ์„ค์ •๋ผ ์žˆ์ง€ ์•Š์œผ๋ฉด 503 ์œผ๋กœ ๋‹ตํ•˜๊ณ , ๊ฑฐ๋ถ€๋œ token ์€ ์…‹ ๋ชจ๋‘์—์„œ ๊ฒฝ๊ณ ๋ฅผ ๋กœ๊ทธ์— ๋‚จ๊ธด๋‹ค

ํ•˜๋‚˜์˜ token ์ด ์„ธ ํ‹ฑ์„ ๋ชจ๋‘ ์—ฐ๋‹ค. ์„ธ endpoint ์ „์ฒด์— ์“ฐ๊ธฐ ๊ถŒํ•œ์„ ์ฃผ๋Š” ๋ฐฐํฌ credential์ด๋‹ค. CronJob ์ด ์–ด๋–ค schedule ์ด ๋„๋ž˜ํ–ˆ๋Š”์ง€ ๋ฌผ์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ฃผ๋Š” ๊ทธ ๋ฌธ์ž์—ด์ด plugins sync ๋„ ์‹คํ–‰ํ•˜๊ณ , ๊ทธ sync ๋Š” ๋‘ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ skill ๊ณผ MCP ์„œ๋ฒ„. ๋ฅผ ๋ชจ๋‘ ์“ด๋‹ค. ์ €์žฅ์†Œ๊ฐ€ ์„ ์–ธํ•œ ์ด๋ฆ„์„ ์ฑ„ํƒํ•˜๊ณ  provenance ๋ฅผ ๊ทธ๊ฒƒ์œผ๋กœ ๋‹ค์‹œ ์“ด๋‹ค. ๊ทธ๊ฒƒ์€ ํ”„๋กœ๋ธŒ๊ฐ€ ์•„๋‹ˆ๋ผ ์“ฐ๊ธฐ ์ž๊ฒฉ ์ฆ๋ช…์œผ๋กœ ๋ฒ”์œ„๋ฅผ ์žก๊ณ  ํšŒ์ „์‹œ์ผœ๋ผ.

trigger ์‹œํฌ๋ฆฟ์€ ํ™œ์„ฑํ™” ํ”Œ๋ž˜๊ทธ๋ฅผ ์ฝ๊ธฐ ์ „์— ๋น„๊ต๋œ๋‹ค. ๋น„ํ™œ์„ฑ trigger ๊ฐ€ ํ‹€๋ฆฐ ์‹œํฌ๋ฆฟ์— ํ™œ์„ฑ trigger ์™€ ๋‹ค๋ฅด๊ฒŒ ๋‹ตํ•  ์ˆ˜ ์—†๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋‹ค. ๊ทธ ์ฐจ์ด๋Š” ์–ด๋–ค trigger ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์˜ค๋ผํด์ด๋‹ค.

์ƒ์ˆ˜ ์‹œ๊ฐ„ ๋น„๊ต๋Š” ์†Œ์œ ์ž๊ฐ€ ํ•˜๋‚˜, src/shared/timingSafe.ts ์ด๊ณ  tests/architecture.test.ts ๊ฐ€ ๊ณ ์ •ํ•œ๋‹ค.

๋ฆฌํ”Œ๋ ˆ์ด ์–ต์ œ๋Š” Slack event_id, Telegram์˜ agentยทbotยทupdate_id, Teams์˜ agentยทApp IDยทconversationยทactivity ID๋ฅผ ํ‚ค๋กœ ํ•œ ์กฐ๊ฑด๋ถ€ claim์„ ์‚ฌ์šฉํ•œ๋‹ค. ์™„๋ฃŒ claim์€ ์ค‘๋ณต์„ ๋ง‰๊ณ , ์‹คํŒจ ๋˜๋Š” ๋งŒ๋ฃŒ๋œ ์ฒ˜๋ฆฌ lease๋Š” ์žฌ์ „๋‹ฌ ์‹œ ๋‹ค์‹œ claimํ•  ์ˆ˜ ์žˆ๋‹ค. ACK ํ›„ ์‹คํ–‰ํ•˜๋Š” ๊ณผ์ •๊ณผ ์™ธ๋ถ€ ๋„๊ตฌ ํšจ๊ณผ๋ฅผ ํ•˜๋‚˜์˜ transaction์œผ๋กœ ๋ฌถ์ง€๋Š” ์•Š์œผ๋ฏ€๋กœ end-to-end exactly-once๋ฅผ ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. ํ”Œ๋žซํผ์ด ์žฌ์ „๋‹ฌํ•˜์ง€ ์•Š์œผ๋ฉด ์œ ์‹ค ์ด๋ฒคํŠธ๋ฅผ ์Šค์Šค๋กœ ๋ณต๊ตฌํ•˜๋Š” worker๋„ ์—†๋‹ค. Agent webhook์˜ ๋ฉฑ๋“ฑ ๊ณ„์•ฝ์€ Trigger ์„ค๊ณ„๋ฅผ ๋”ฐ๋ฅธ๋‹ค.

Teams signing metadata๋Š” HTTP ์„ฑ๊ณต ์‘๋‹ต๋งŒ ์‚ฌ์šฉํ•œ๋‹ค. ์กฐํšŒ ์‹คํŒจ๋„ ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ์— ํฌํ•จํ•˜๋ฉฐ ๋งŒ๋ฃŒ๋œ ํ‚ค๋ฅผ ์žฅ์•  ์ค‘ ์ธ์ฆ์— ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ์กฐํšŒยท์บ์‹œ์˜ ํ˜„์žฌ ๊ณ„์•ฝ์€ Teams ์„ค๊ณ„๋ฅผ ๋”ฐ๋ฅธ๋‹ค.

์ธ๋ฐ”์šด๋“œ ์š”์ฒญ ํฌ๊ธฐ

JSON ๋ณธ๋ฌธ์€ schema ๊ฒ€์ฆ ์ „์— bounded reader๋ฅผ ์ง€๋‚œ๋‹ค. ๊ด€๋ฆฌยทํŽธ์ง‘ ์š”์ฒญ์€ Skill ์ „์ฒด ํŒŒ์ผ ํ•œ๋„์—์„œ ํŒŒ์ƒํ•œ editor ํ•œ๋„, ์ด๋ฏธ์ง€ยท๋ฌธ์„œ๋ฅผ ์‹ค์„ ์ˆ˜ ์žˆ๋Š” ์‹คํ–‰ ์š”์ฒญ์€ attachment ํ•œ๋„์—์„œ ํŒŒ์ƒํ•œ turn ํ•œ๋„๋ฅผ ์“ด๋‹ค. ๋ชจ๋ธ ๋“ฑ๋ก์€ editor ํ•œ๋„๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. webhook ๋„ค ์ข…๋ฅ˜๋Š” ์„œ๋ช… ๊ฒ€์ฆ์— ํ•„์š”ํ•œ raw ๋ณธ๋ฌธ์„ ๊ณตํ†ต 1MB ํ•œ๋„ ์•„๋ž˜์—์„œ ์ฝ๋Š”๋‹ค. ์„ ์–ธ๋œ Content-Length๊ฐ€ ํ•œ๋„๋ฅผ ๋„˜์œผ๋ฉด body๋ฅผ ์ฝ์ง€ ์•Š๊ณ  413์„ ๋‹ตํ•˜๊ณ , chunked body๋Š” ๋ˆ„์  ๋ฐ”์ดํŠธ๊ฐ€ ํ•œ๋„๋ฅผ ๋„˜๋Š” ์ฆ‰์‹œ stream์„ ์ทจ์†Œํ•œ๋‹ค. 256KiB prose allowance๋ฅผ ๋„˜๋Š” ํฐ ์‹คํ–‰ ๋ณธ๋ฌธ์€ ํ”„๋กœ์„ธ์Šค ๋‹จ์œ„์˜ ๋ฐ”์ดํŠธ ์˜ˆ์‚ฐ์— ๊ณผ๊ธˆ๋œ๋‹ค. ์˜ˆ์‚ฐ์€ ์ตœ๋Œ€ turn ๋ณธ๋ฌธ ๋‘ ๊ฐœ ๋ถ„๋Ÿ‰์ด๊ณ , ์š”์ฒญ์€ ์ž๊ธฐ๊ฐ€ ์‹ค์ œ๋กœ ์ฝ์€ ๋ฐ”์ดํŠธ๋งŒํผ๋งŒ ์“ด๋‹ค. ๊ณผ๊ธˆ์€ ํŒŒ์‹ฑ๋ถ€ํ„ฐ run ๋˜๋Š” stream์ด ์ž…๋ ฅ์„ ๋†“์„ ๋•Œ๊นŒ์ง€ ์œ ์ง€๋˜๊ณ , ์—ฐ๊ฒฐ์—์„œ ๋ถ„๋ฆฌ๋˜์–ด ๊ณ„์† ๋„๋Š” chat์€ ๋‚ด๋ถ€ drain ์™„๋ฃŒ๊นŒ์ง€ ์œ ์ง€ํ•œ๋‹ค. ์ผ๋ฐ˜ text turn์€ ์•„๋ฌด๊ฒƒ๋„ ์“ฐ์ง€ ์•Š๋Š”๋‹ค. ์˜ˆ์‚ฐ์ด ๋ชจ์ž๋ผ๋ฉด body๋ฅผ ์ทจ์†Œํ•œ ๋’ค Retry-After๋ฅผ ํฌํ•จํ•œ 429๋ฅผ ๋‹ตํ•œ๋‹ค.

์˜ˆ์‚ฐ์€ ์š”์ฒญ ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ๋ฉ”๋ชจ๋ฆฌ์— ์œ ์ง€ํ•˜๋Š” ํฐ ๋ณธ๋ฌธ์˜ ๋ฐ”์ดํŠธ๋ฅผ ์„ผ๋‹ค. ์ •ํ™•ํ•œ ์ƒํ•œ๊ณผ ์†Œ์œ  ํŒŒ์ผ์€ ๊ณ ์ • ์ œํ•œ์„ ๋”ฐ๋ฅธ๋‹ค.

tests/architecture.test.ts๋Š” API route์˜ ์ง์ ‘ request.json()๊ณผ request.formData() ํ˜ธ์ถœ์„ ๊ฑฐ๋ถ€ํ•œ๋‹ค. Zod์˜ ํ•„๋“œ ํฌ๊ธฐ ๊ฒ€์‚ฌ๋Š” ํŒŒ์‹ฑ ๋’ค์˜ ๊ฐ’ ๊ทœ์น™์ด์ง€, ํŒŒ์‹ฑ ์ „์— ๋ฐœ์ƒํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น ์ œํ•œ์ด ์•„๋‹ˆ๋‹ค.

Session mutation๊ณผ CSRF

Cookie session์œผ๋กœ ์ธ์ฆํ•˜๋Š” POSTยทPUTยทPATCHยทDELETE๋Š” Origin์ด request origin ๋˜๋Š” ์„ค์ •๋œ PUBLIC_BASE_URL origin๊ณผ ์ •ํ™•ํžˆ ๊ฐ™์•„์•ผ ํ•œ๋‹ค. ๋ฉ€ํ‹ฐ ๋„๋ฉ”์ธ์—์„œ๋Š” ํ—ˆ์šฉ ๋ชฉ๋ก์— ์žˆ๋Š” ์š”์ฒญ Host์™€ ์ธ์ฆ ์„ค์ •์˜ ํ”„๋กœํ† ์ฝœ๋กœ ๋งŒ๋“  origin๊ณผ ์ •ํ™•ํžˆ ๊ฐ™์•„์•ผ ํ•˜๋ฉฐ, ๋‹ค๋ฅธ ํ—ˆ์šฉ ๋„๋ฉ”์ธ์˜ origin๋„ ๊ฑฐ๋ถ€ํ•œ๋‹ค. Origin์ด ์—†๊ฑฐ๋‚˜ null์ด๊ฑฐ๋‚˜ URL๋กœ ํ•ด์„๋˜์ง€ ์•Š์œผ๋ฉด 403์ด๋‹ค. ์„ธ session wrapper๊ฐ€ ์ผ๋ฐ˜ console API๋ฅผ ํ•œ ๋ฒˆ์— ๋ณดํ˜ธํ•˜๊ณ , agent ์‹คํ–‰ API๋Š” Authorization ํ—ค๋”๊ฐ€ ์—†์„ ๋•Œ๋งŒ cookie session์„ ์‚ฌ์šฉํ•˜๋ฉฐ ๊ฐ™์€ ๊ฒ€์‚ฌ๋ฅผ ์ ์šฉํ•œ๋‹ค. bearer token, webhook signature์ฒ˜๋Ÿผ cookie๋ฅผ ์“ฐ์ง€ ์•Š๋Š” ๋จธ์‹  ํ˜ธ์ถœ์—๋Š” CSRF ๊ฒ€์‚ฌ๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ ๋ฐ–์˜ origin๊ณผ ์•ฑ์ด ๋ณด๋Š” request origin์ด ๋‹ค๋ฅด๋ฉด ๋‹จ์ผ ๋„๋ฉ”์ธ์€ PUBLIC_BASE_URL์„ ์„ค์ •ํ•˜๊ณ , ๋ฉ€ํ‹ฐ ๋„๋ฉ”์ธ์€ BETTER_AUTH_ALLOWED_HOSTS๋ฅผ ์„ค์ •ํ•œ ๋’ค ์›๋ž˜ Host๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

์‘๋‹ต ํ—ค๋”

next.config.ts๋Š” ์ผ๋ฐ˜ ๊ฒฝ๋กœ์— framing ์ฐจ๋‹จ CSP, X-Frame-Options: DENY, nosniff, strict-origin-when-cross-origin์„ ์ ์šฉํ•œ๋‹ค. ์ผ๋ฐ˜ ์ฝ˜์†”์—๋Š” script/style์˜ ๋กœ๋“œ๋ฅผ ์ œํ•œํ•˜๋Š” CSP๊ฐ€ ์—†์œผ๋ฏ€๋กœ ์ด ์ •์ฑ…์ด script injection๊นŒ์ง€ ์ฐจ๋‹จํ•œ๋‹ค๊ณ  ๋ณด์ง€ ์•Š๋Š”๋‹ค.

/api/artifacts/{id}/view์™€ /api/objects/*๋Š” ์ด ๊ทœ์น™์—์„œ ์ œ์™ธํ•˜๊ณ  ๊ฐ route๊ฐ€ ์ž์ฒด sandbox ์ •์ฑ…์„ ์„ค์ •ํ•œ๋‹ค. ์˜ค๋ฅ˜ ์‘๋‹ต์—๋„ ํ—ค๋”๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ๋ผ์šฐํŠธ์˜ CSP๋ฅผ ๋ฐ”๊ฟ€ ๋•Œ๋Š” ์ „์—ญ ํ—ค๋”๊ฐ€ ๋ฎ์–ด์“ฐ์ง€ ์•Š๋Š”์ง€ ์‹ค์ œ ์‘๋‹ต์„ ํ™•์ธํ•œ๋‹ค. ํŒŒ์ผ๋ณ„ ์ •์ฑ…์€ ๋ฐ์ดํ„ฐ ๋…ธ์ถœ๊ณผ ๋ณด์กด์„ ๋”ฐ๋ฅธ๋‹ค.

์•„์›ƒ๋ฐ”์šด๋“œ ์š”์ฒญ (SSRF)

์šด์˜์ž๊ฐ€ ๋“ฑ๋กํ•œ MCP ์„œ๋ฒ„ URL์€ src/infrastructure/net/ssrfGuard.ts๊ฐ€ ๋“ฑ๋ก ์‹œ์ ๊ณผ ๋””์ŠคํŒจ์น˜ ์‹œ์  ๋ชจ๋‘ ์—์„œ ๊ฒ€์ฆํ•œ๋‹ค. ๊ฑฐ๋ถ€๋˜๋Š” ๊ฒƒ: http(s) ๊ฐ€ ์•„๋‹Œ ์Šคํ‚ด, userinfo ๋ฅผ ์‹ค์€ URL(https://user:pass@host, ์ฃผ์†Œ ์•ˆ์˜ ์ž๊ฒฉ ์ฆ๋ช…์€ ์—ฌ๊ธฐ์„œ ๋ฌด์–ธ๊ฐ€๋ฅผ ์ธ์ฆํ•˜๋Š” ๋ฐฉ์‹์ด ์•„๋‹ˆ๊ณ , ํ˜ธ์ŠคํŠธ๋ฅผ ๋‹ค๋ฅธ ๊ฒƒ์ฒ˜๋Ÿผ ์ฝํžˆ๊ฒŒ ๋งŒ๋“œ๋Š” ์ƒํˆฌ์  ์ˆ˜๋‹จ์ด๋‹ค), ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์„คยท๋ฃจํ”„๋ฐฑยท๋งํฌ๋กœ์ปฌ(ํด๋ผ์šฐ๋“œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ฃผ์†Œ 169.254.169.254 ํฌํ•จ) ๋˜๋Š” ๊ทธ ๋ฐ–์˜ ์˜ˆ์•ฝ ๋Œ€์—ญ์œผ๋กœ ํ•ด์„๋˜๋Š” ํ˜ธ์ŠคํŠธ. DNS ์กฐํšŒ๊ฐ€ ์˜ˆ์™ธ๋กœ ์‹คํŒจํ•˜๋ฉด ์ฃผ์†Œ ๊ฑฐ๋ถ€๋กœ ๋ฐ”๊พธ์ง€ ์•Š๊ณ  ํ˜ธ์ถœ ์‹คํŒจ๋กœ ์ „๋‹ฌํ•œ๋‹ค. ์กฐํšŒ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋๋‚ฌ์ง€๋งŒ ์ฃผ์†Œ๊ฐ€ ์—†๊ฑฐ๋‚˜ ๊ธˆ์ง€ ๋Œ€์—ญ์„ ํฌํ•จํ•˜๋ฉด ๊ฑฐ๋ถ€ํ•œ๋‹ค.

IPv6 ๋Š” ์ฃผ์†Œ ํ•˜๋‚˜์— ์ฒ ์ž๊ฐ€ ์—ฌ๋Ÿฟ์ด๋ฏ€๋กœ, ํ…์ŠคํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ 8๊ฐœ ๊ทธ๋ฃน์œผ๋กœ ํŽผ์นœ ๊ฐ’์œผ๋กœ ํŒ์ •ํ•œ๋‹ค. IPv4 ๋ฅผ ์•ˆ์— ๋‹ด๋Š” ์ ‘๋‘์‚ฌ(IPv4-mapped, IPv4-compatible, NAT64 64:ff9b::/96, 6to4 2002::/16)๋Š” ํ†ต์งธ๋กœ ๋ง‰์ง€ ์•Š๊ณ  ๋‹ด๊ธด IPv4 ๋กœ ํŒ์ •ํ•œ๋‹ค โ€” IPv6 ์ „์šฉ ๋ง์—์„œ ๊ณต์ธ ์ฃผ์†Œ์— ๋‹ฟ๋Š” ์ •์ƒ ๊ฒฝ๋กœ๊ฐ€ ๊ทธ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 64:ff9b::8.8.8.8 ์€ ํ†ต๊ณผํ•˜๊ณ  64:ff9b::10.0.0.1 ์€ ๊ฑฐ๋ถ€๋œ๋‹ค.

์ด registry์˜ ๊ณต๊ฐœ ์ฃผ์†Œ ๋””์ŠคํŒจ์น˜๋Š” fetchPublicUrl(src/infrastructure/net/publicFetch.ts)์„ ์ง€๋‚œ๋‹ค. ๋ฐฐํฌ๊ฐ€ ์ง€์ •ํ•œ LLMยท์ธ์ฆยท์Šคํ† ๋ฆฌ์ง€ยท์นดํƒˆ๋กœ๊ทธ endpoint๊นŒ์ง€ ๋ชจ๋‘ ์ด ๊ฐ€๋“œ๋กœ ๊ฒ€์‚ฌํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ๊ทธ ์ฃผ์†Œ๋“ค์€ ๋ฐฐํฌ ์„ค์ •์˜ ์‹ ๋ขฐ ๊ฒฝ๊ณ„๋‹ค.

LLM ์ œ๊ณต์ž ์š”์ฒญ์€ src/infrastructure/llm/providerFetch.ts์—์„œ ๋ชจ๋“  redirect๋ฅผ ๊ฑฐ๋ถ€ํ•œ๋‹ค. SDK์˜ ์ฑ„ํŒ…ยท์ž„๋ฒ ๋”ฉยท์ด๋ฏธ์ง€ ์š”์ฒญ, SigV4, decisionยทrerankยท์˜ค๋””์˜ค, Workspace์˜ native ์š”์ฒญ๊ณผ ์ œ๊ณต์ž discoveryยท์—ฐ๊ฒฐ probe๊ฐ€ ๊ฐ™์€ ๊ทœ์น™์„ ์ ์šฉํ•œ๋‹ค. ๋”ฐ๋ผ์„œ 307/308 ์‘๋‹ต์œผ๋กœ ํ”„๋กฌํ”„ํŠธยท๋ฌธ์„œยท ์˜ค๋””์˜คยท์ž๊ฒฉ ์ฆ๋ช…์„ ๋‹ค๋ฅธ ์ฃผ์†Œ์— ๋‹ค์‹œ ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค. ์ œ๊ณต์ž๋Š” ์ตœ์ข… endpoint๋กœ ๋“ฑ๋กํ•œ๋‹ค.

๊ณต๊ฐœ registry ์š”์ฒญ์—๋Š” ๋‹ค์Œ ๊ฒ€์‚ฌ๋ฅผ ์ ์šฉํ•œ๋‹ค:

  • DNS๋Š” ๋ชจ๋“  ์š”์ฒญ๊ณผ redirect ํ™‰์—์„œ ๋‹ค์‹œ ํ™•์ธํ•˜๊ณ  ์ปค๋„ฅ์…˜์„ ๊ฒ€์ฆํ•œ ์ฃผ์†Œ์— ๊ณ ์ •ํ•œ๋‹ค. ๋“ฑ๋ก ์‹œ ๊ฒ€์ฆ๋งŒ์œผ๋กœ ์—ฐ๊ฒฐ์˜ ์•ˆ์ „์„ ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ๋„ค์ดํ‹ฐ๋ธŒ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์ถ”์ข…์€ ๊บผ์ ธ ์žˆ๊ณ  ๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋Š” ๊ฑฐ๋ถ€ํ•œ๋‹ค. ์ €์žฅ๋œ ์ž๊ฒฉ ์ฆ๋ช…์ด ๋‹ค๋ฅธ ํ˜ธ์ŠคํŠธ๋กœ ์ „๋‹ฌ๋  ์ˆ˜ ์—†๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋‹ค.
  • ๋””์ŠคํŒจ์ฒ˜๋Š” ์ปค๋„ฅ์…˜ ์žฌ์‚ฌ์šฉ์„ ์œ„ํ•ด origin|pinned address ๋ณ„๋กœ ํ’€๋ง๋œ๋‹ค. ์ด๊ฒƒ์ด ์บ์‹œํ•˜๋Š” ๊ฒƒ์€ ์ „์†ก ๊ณ„์ธต๋ฟ ์ด๋‹ค. ๊ฐ€๋“œ๋Š” ์—ฌ์ „ํžˆ ์š”์ฒญ๋งˆ๋‹ค ๋Œ๊ธฐ ๋•Œ๋ฌธ์—, ์‚ฌ์„ค ์ฃผ์†Œ๋กœ ํ•ด์„๋˜๊ธฐ ์‹œ์ž‘ํ•œ ํ˜ธ์ŠคํŠธ๋Š” ํ’€๋ง๋œ ๋””์ŠคํŒจ์ฒ˜์— ๋‹ฟ๊ธฐ ์ „์— ๊ฑฐ๋ถ€๋˜๊ณ , ๋‹ค๋ฅธ ๊ณณ์œผ๋กœ ํ•ด์„๋˜๋Š” ํ˜ธ์ŠคํŠธ๋Š” ๋‹ค๋ฅธ ํ‚ค๋ฅผ ๋ฐ›๋Š”๋‹ค.

Slack ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ์˜ files.getUploadURLExternal ์‘๋‹ต์€ HTTPS files.slack.com/upload/v1/ ์ฃผ์†Œ์ธ์ง€ ํ™•์ธํ•œ ๋’ค ๋ฐ”์ดํŠธ๋ฅผ ๋ณด๋‚ด๋ฉฐ, ์—…๋กœ๋“œ ์š”์ฒญ์˜ redirect๋Š” ๋”ฐ๋ฅด์ง€ ์•Š๋Š”๋‹ค.

๋ชจ๋ธ ์ž…๋ ฅ์˜ ์ด๋ฏธ์ง€๋Š” URL๋กœ ๊ฐ€์ ธ๊ฐ€์ง€ ์•Š๋Š”๋‹ค. ์‹คํ–‰ API๋Š” ์ง€์›ํ•˜๋Š” ์ด๋ฏธ์ง€ ๋ฐ”์ดํŠธ๋ฅผ ์š”์ฒญ ์•ˆ์— ์ธ๋ผ์ธ์œผ๋กœ ๋ฐ›์œผ๋ฉฐ, LLM ์ฑ„๋„์€ ๋ชจ๋“  image_url์ด bounded data: URL์ธ์ง€ ๋งˆ์ง€๋ง‰์œผ๋กœ ๋‹ค์‹œ ํ™•์ธํ•œ๋‹ค. https://๋งŒ ๊ฒ€์‚ฌํ•œ ๋’ค ์›๊ฒฉ URL์„ ์ œ๊ณต์ž์—๊ฒŒ ๊ทธ๋Œ€๋กœ ๋„˜๊ธฐ๋ฉด ์š”์ฒญ์€ ์ด ์•ฑ์ด ์•„๋‹ˆ๋ผ ์ œ๊ณต์ž ๋„คํŠธ์›Œํฌ์—์„œ ๋ฐœ์ƒํ•œ๋‹ค. ๊ทธ ๊ฒฝ๋กœ์—๋Š” fetchPublicUrl์˜ DNSยท์ฃผ์†Œยทredirect ๊ฒ€์‚ฌ๊ฐ€ ๋‹ฟ์ง€ ์•Š์œผ๋ฏ€๋กœ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ชจ๋ธ์ด ์›น์˜ ์ด๋ฏธ์ง€๋ฅผ ์ฝ์–ด์•ผ ํ•˜๋ฉด FetchUrl ๋„๊ตฌ๊ฐ€ ์ด ์•ฑ์˜ ์•„์›ƒ๋ฐ”์šด๋“œ ๊ฒฝ๊ณ„๋กœ ๋ฐ”์ดํŠธ๋ฅผ ๊ฐ€์ ธ์™€ ๊ฐ™์€ inline ํ˜•์‹์œผ๋กœ ๋Œ๋ ค์ค€๋‹ค.

๊ณต๊ฐœ URL ์ด๋ฉด ๋ฌด์—‡์ด๋“  ํ—ˆ์šฉ๋œ๋‹ค. ์‹ ๋ขฐํ•˜๋Š” ์—”๋“œํฌ์ธํŠธ๋งŒ ๋“ฑ๋กํ•˜๋ผ. Registry endpoint URL ์€ userinfo, query parameter ์™€ fragment ๋ฅผ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค. ์ด ๊ฐ’๋“ค์€ ๋ฉค๋ฒ„๊ฐ€ ์ฝ๋Š” registry view ์™€ ์šด์˜ ๋กœ๊ทธ์—์„œ ์ž๊ฒฉ ์ฆ๋ช…์„ ๋…ธ์ถœํ•˜๊ธฐ ์‰ฌ์šฐ๋ฏ€๋กœ, ์ธ์ฆ ์ •๋ณด๋Š” encrypted header ๋˜๋Š” OAuth ์—ฐ๊ฒฐ์— ๋‘”๋‹ค. ์ด์ „ ํ–‰์— ๋‚จ์€ userinfo, query ์™€ fragment ๋Š” ์ €์žฅ๋œ ์ฃผ์†Œ์—๋งŒ ๋‚จ๊ณ  reader-facing view ์—์„œ๋Š” ์ œ๊ฑฐํ•œ๋‹ค. ์ด ๊ทœ์น™์€ ์ฃผ์†Œ๊ฐ€ ์‹ค์ œ๋กœ ๋ฐ”๋€” ๋•Œ๋งŒ ์ ์šฉํ•œ๋‹ค. ์ €์žฅ๋œ ์ฃผ์†Œ๋ฅผ ๊ทธ๋Œ€๋กœ, ๋˜๋Š” ์ฝ˜์†”์ด ๋ณด์—ฌ ์ค€ redacted ํ˜•ํƒœ๋กœ ๋˜๋Œ๋ ค ๋ณด๋‚ด๋Š” ์ €์žฅ์€ ์ด๋™์ด ์•„๋‹ˆ๋ฏ€๋กœ ๊ฑฐ์ ˆํ•˜์ง€๋„, ์ €์žฅ๋œ credential ์„ ๋ฒ„๋ฆฌ์ง€๋„ ์•Š๋Š”๋‹ค (resolveRegistryUrlPatch). ๊ทธ๋Ÿฌ์ง€ ์•Š์œผ๋ฉด ํŽธ์ง‘ ํผ์ด ์ž๊ธฐ๊ฐ€ ์ฝ์€ ๊ฐ’์„ ๋˜๋Œ๋ ค ๋ณด๋‚ด๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ๋ ˆ๊ฑฐ์‹œ ํ•ญ๋ชฉ์ด ๋‹ค๋ฅธ endpoint ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ๋˜๊ณ , ์›๋ž˜ ์ฃผ์†Œ๋Š” ๋‹ค์‹œ ์ž…๋ ฅํ•  ์ˆ˜๋„ ์—†๋‹ค. LLM provider์˜ ์ด๋ฆ„ยท์ข…๋ฅ˜ยทendpointยท์ธ์ฆ ๋ฐฉ์‹๊ณผ credential์€ ํ•œ ๋ณด์•ˆ ๋‹จ์œ„๋‹ค. ๋“ฑ๋ก provider์˜ endpointยท์ข…๋ฅ˜ยท์ธ์ฆ ๋ฐฉ์‹์„ ๋ฐ”๊พธ๋ฉด ๊ธฐ์กด key์˜ ๋งˆ์Šคํฌยท๋นˆ ์ž…๋ ฅ์œผ๋กœ ์˜ฎ๊ธธ ์ˆ˜ ์—†๋‹ค. key๊ฐ€ ํ•„์š”ํ•œ ์ƒˆ ์—ฐ๊ฒฐ์—๋Š” ์ƒˆ ๊ฐ’์„ ์ž…๋ ฅํ•˜๋ฉฐ SigV4๋Š” API key ์—†์ด AWS credential chain์„ ์‚ฌ์šฉํ•œ๋‹ค. ์‹คํ–‰ ๋ชจ๋ธ์€ Settings์—์„œ ์„ ํƒํ•œ ๋“ฑ๋ก ๋ชจ๋ธ์ด์–ด์•ผ ํ•œ๋‹ค. ๊ธฐ๋ณธ ๋ชจ๋ธ ์„ ํƒ์ด ๋ณ„๋„ ๊ธฐ๋ณธ ์ฑ„๋„์„ ๋งŒ๋“ค์ง€๋Š” ์•Š๋Š”๋‹ค.

MCP ํด๋ผ์ด์–ธํŠธ๋Š” @modelcontextprotocol/client ์œ„์—์„œ ๋Œ๊ณ , ๊ฐ€๋“œ๋Š” ๊ทธ ์˜†์— ๋†“์ด๋Š” ๋Œ€์‹  ๊ทธ ์•ˆ์œผ๋กœ ์ฃผ์ž…๋œ๋‹ค. ํŠธ๋žœ์ŠคํฌํŠธ์— fetch ๋กœ ์ฃผ์–ด์ง€๋Š” ๊ฒƒ์ด boundedFetch(src/infrastructure/mcp/session.ts)์ด๋ฏ€๋กœ ํ”„๋กœ๋ธŒ, ํ•ธ๋“œ์…ฐ์ดํฌ, ๋ชจ๋“  ๋„๊ตฌ ํ˜ธ์ถœ, ์„ธ์…˜ ํ•ด์ œ๊นŒ์ง€ ์ „๋ถ€ ๊ฐ™์€ ๊ฒฝ๊ณ„๋ฅผ ์ง€๋‚œ๋‹ค. SDK ๋ฅผ ์ž๊ธฐ fetch ์— ๋งก๊ฒจ ๋‘๋ฉด ์šด์˜์ž๊ฐ€ ๋„ฃ์€ URL ์„ ๊ณง์žฅ ๋„คํŠธ์›Œํฌ๋กœ ๊ฐ€์ ธ๊ฐˆ ๊ฒƒ์ด๋‹ค. ๊ฐ™์€ ๋ž˜ํผ๊ฐ€ ์‘๋‹ต ๋ฐ”์ดํŠธ ์ƒํ•œ๋„ ํ•จ๊ป˜ ๋‚˜๋ฅด๋Š”๋ฐ, SDK ์—๋Š” ๊ทธ๋Ÿฐ ๊ฐœ๋… ์ž์ฒด๊ฐ€ ์—†๋‹ค.

์„ ์–ธ๋œ ๋‚ด๋ถ€ ํ˜ธ์ŠคํŠธ

ํด๋Ÿฌ์Šคํ„ฐ์—์„œ ์ด ์•ฑ์ด ๋ถ€๋ฅด๊ธฐ๋กœ ๋˜์–ด ์žˆ๋Š” MCP ์„œ๋ฒ„๋“ค์€ ๊ตฌ์กฐ์ƒ ์‚ฌ์„ค์ด๋‹ค. Kubernetes Service ๋Š” ๊ฐ€๋“œ๊ฐ€ ๊ฑฐ๋ถ€ํ•˜๋Š” ClusterIP ๋กœ ํ•ด์„๋œ๋‹ค. MCP_INTERNAL_HOST_SUFFIXES ๋Š” ๋ฐฐํฌ๊ฐ€ ๊ทธ ์ด๋ฆ„๋“ค์ด ๋ฌด์—‡์ธ์ง€ ๋ฐํžˆ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค:

MCP_INTERNAL_HOST_SUFFIXES=agent-mcps.svc.cluster.local

๋กœ์ปฌ ์•ฑ์˜ MCP๋ฅผ ์—ฐ๊ฒฐํ•˜๋ ค๋ฉด localhost๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ถ”๊ฐ€ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด MCP_INTERNAL_HOST_SUFFIXES=agent-mcps.svc.cluster.local,localhost๋Š” http://localhost:3100/api/organizations/opspresso/mcp๋ฅผ ํ—ˆ์šฉํ•œ๋‹ค. localhost๋Š” ์ •ํ™•ํžˆ ๊ทธ ํ˜ธ์ŠคํŠธ๋งŒ ํ—ˆ์šฉํ•˜๋ฉฐ ํ•˜์œ„ ๋„๋ฉ”์ธ๊ณผ IP ์ฃผ์†Œ๋Š” ํฌํ•จํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ ๊ณ„์† ์ฐจ๋‹จ์ด๋ฉฐ, ์ด MCP ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด๋„ ๋ชจ๋ธ์˜ FetchUrl ํ—ˆ์šฉ ๋ชฉ๋ก์€ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค.

์„ ์–ธ๋œ ์ ‘๋ฏธ์‚ฌ ์•„๋ž˜์˜ ํ˜ธ์ŠคํŠธ๋Š” ๊ทธ ์งˆ๋ฌธ์ด ๋˜์ ธ์ง€๋Š” ๋ชจ๋“  ๊ณณ์—์„œ public URL ๊ฐ€๋“œ๋ฅผ ๊ฑด๋„ˆ๋›ด๋‹ค. ํ•ญ๋ชฉ์„ ๋“ฑ๋กํ•  ๋•Œ์™€ ํŽธ์ง‘ํ•  ๋•Œ, ์ฝ˜์†”์˜ "Test connection" ํ”„๋กœ๋ธŒ, admin ์ด ํ•ญ๋ชฉ์˜ OAuth ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์„ ๋•Œ, agent ์ž์‹ ์˜ ๋„๊ตฌ ๋ชฉ๋ก, ๊ทธ๋ฆฌ๊ณ  ๋””์ŠคํŒจ์น˜. ๊ฐ๊ฐ skipsUrlGuard ๋ฅผ ํ†ตํ•ด์„œ๋‹ค. ์ฐจ๋‹จ๋œ ์ฃผ์†Œ ๋Œ€์—ญ์ด ๋„“์–ด์ง€๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ชจ๋“  ํ•ญ๋ชฉ์€ ์—ฌ์ „ํžˆ ์›๋ž˜ ๋ฐ›๋˜ ๊ฒ€์‚ฌ๋ฅผ ๋ฐ›๋Š”๋‹ค. ์ด๊ฒƒ์€ ๊ฐ€๋“œ๋ฅผ ๋А์Šจํ•˜๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ managed ๋ฃจํ”„๋ฐฑ ์˜†์— ๋†“์ธ ๋‘ ๋ฒˆ์งธ ์ข์€ ์˜ˆ์™ธ๋‹ค.

๋‚ด๋ถ€ ์ฃผ์†Œ ์˜ˆ์™ธ๋„ ์ž๋™ redirect ์ถ”์ข…์€ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. MCP ์„ธ์…˜ยทOAuth ๋ฉ”ํƒ€๋ฐ์ดํ„ฐยท๋‚ด๋ถ€ FetchUrl์€ src/infrastructure/net/redirectPolicy.ts์˜ fetchSameOrigin์„ ํ†ตํ•ด ์ตœ๋Œ€ 5ํšŒ๋งŒ ๊ฐ™์€ ์ถœ์ฒ˜๋กœ ์ด๋™ํ•œ๋‹ค. ๊ณต๊ฐœ URL๋„ ๊ฐ™์€ redirect ๊ทœ์น™์„ ์‚ฌ์šฉํ•˜๋ฉฐ, ๊ฐ ์š”์ฒญ ์ง์ „์— DNS๋ฅผ ๊ฒ€์ฆํ•˜๊ณ  ํ™•์ธ๋œ ์ฃผ์†Œ๋กœ ์—ฐ๊ฒฐํ•œ๋‹ค. ๋‹ค๋ฅธ ์ถœ์ฒ˜๋กœ ์ด๋™ํ•˜๋Š” ์‘๋‹ต์€ ๋ณธ๋ฌธ์„ ํ•ด์ œํ•œ ๋’ค ๊ฑฐ๋ถ€ํ•œ๋‹ค.

์ด๋ฆ„์„ ๋งž์ถ”๋Š” ์ˆ ์–ด๋Š” ํ•˜๋‚˜๋‹ค. src/domain/security/internalHosts.ts ์˜ isDeclaredInternalHost. ๊ทธ๋ฆฌ๊ณ  ๋ชฉ๋ก์€ ๋‘˜์ด๋‹ค. MCP_INTERNAL_HOST_SUFFIXES ๋Š” ์ด ์•ฑ์ด ๋ถ€๋ฅด๊ธฐ๋กœ ๋˜์–ด ์žˆ๋Š” ์„œ๋น„์Šค๋ฅผ, URL_FETCH_INTERNAL_HOST_SUFFIXES ๋Š” ๋ชจ๋ธ์ด ์ฝ์–ด๋„ ๋˜๋Š” ํŽ˜์ด์ง€๋ฅผ ๋งํ•˜๊ณ , ๋‘ ๋ชฉ๋ก์€ ์˜๋„์ ์œผ๋กœ ์„œ๋กœ๋ฅผ ๋ชจ๋ฅธ๋‹ค. ์ˆ ์–ด๊ฐ€ domain ์˜ ์ค‘๋ฆฝ ๋ชจ๋“ˆ์— ์žˆ๋Š” ์ด์œ ๊ฐ€ ๊ทธ๊ฒƒ์ด๋‹ค: skipsUrlGuard(MCP ์ชฝ, provenance ์™€ ํ•ฉ์นœ ํ˜•ํƒœ) ์˜†์— ๋‘๋ฉด FetchUrl ๋ฆฌ๋”๊ฐ€ MCP ๋ชจ๋“ˆ์„ import ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ๊ทธ ๋ฆฌ๋”๋Š” MCP ๋ชฉ๋ก์„ ์ฝ์–ด์„œ๋Š” ์•ˆ ๋œ๋‹ค.

๊ทธ ์ข์Œ์ด ์„ค๊ณ„์˜ ์ „๋ถ€์ด๊ณ , ๊ฐ ๋ถ€๋ถ„์€ tests/internalHosts.test.ts ๊ฐ€ ๊ณ ์ •ํ•œ๋‹ค:

  • ์„ค์ •์œผ๋กœ๋งŒ. ๋ชฉ๋ก์€ ํ™˜๊ฒฝ์—์„œ ์˜จ๋‹ค. ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ํ•ญ๋ชฉ์ด ์ž๊ธฐ ์˜ˆ์™ธ๋ฅผ ์Šค์Šค๋กœ ์ง€์ •ํ•  ์ˆ˜ ์—†๊ณ , ์˜๋„์ ์œผ๋กœ ๋Ÿฐํƒ€์ž„ ์„ค์ •์ด ์•„๋‹ˆ๋‹ค. ์•„์›ƒ๋ฐ”์šด๋“œ ๊ฒฝ๊ณ„๋ฅผ ๋„“ํžˆ๋Š” ์ผ์€ ๊ทธ๋•Œ admin ์„ ์ฅ” ์‚ฌ๋žŒ์ด ์ œ์ถœํ•˜๋Š” ํผ์ด ์•„๋‹ˆ๋ผ ๋ฐฐํฌ๋ฅผ ๊ฑฐ์ณ์•ผ ํ•œ๋‹ค.
  • ๋ ˆ์ด๋ธ” ๋‹จ์œ„๋กœ ๊ณ ์ •. agent-mcps.svc.cluster.local ์€ mcp-url-fetch.agent-mcps.svc.cluster.local ์„ ํ—ˆ์šฉํ•˜๊ณ  evil-agent-mcps.svc.cluster.local ์€ ๊ฑฐ๋ถ€ํ•œ๋‹ค. ๋‹จ์ˆœํ•œ "โ€ฆ๋กœ ๋๋‚œ๋‹ค"์˜€๋‹ค๋ฉด ํ†ต๊ณผ์‹œ์ผฐ์„ ์•„์Šฌ์•„์Šฌํ•œ ๊ฒฝ์šฐ๋‹ค. ์•ž์— ๋ถ™์€ ์ ์€ ํ—ˆ์šฉ๋˜๋ฉฐ ๊ฐ™์€ ๋œป์ด๋‹ค.
  • ๋‹จ์ผ ๋ ˆ์ด๋ธ” ์ ‘๋ฏธ์‚ฌ๋Š” ์—†๋‹ค. local ์ด๋‚˜ internal ์€ ์ด๋ฆ„ ๊ณต๊ฐ„ ํ•˜๋‚˜๋ฅผ ํ†ต์งธ๋กœ ํ—ˆ์šฉํ•˜๊ฒŒ ๋œ๋‹ค. ์˜๋„๋ผ๊ธฐ๋ณด๋‹ค ์˜คํƒ€์ผ ๊ฐ€๋Šฅ์„ฑ์ด ํ›จ์”ฌ ๋†’์œผ๋ฏ€๋กœ ์กด์ค‘ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ช…์‹œํ•œ localhost๋งŒ ์ •ํ™•ํ•œ ํ˜ธ์ŠคํŠธ ์ผ์น˜๋กœ ํ—ˆ์šฉํ•˜๋ฉฐ, ์ ‘๋ฏธ์‚ฌ๋กœ ํ™•์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • IP ๋ฆฌํ„ฐ๋Ÿด์€ ๊ฒฐ์ฝ” ์•ˆ ๋œ๋‹ค. ๊ทธ ์˜ˆ์™ธ๋Š” ๋ˆ„๊ตฐ๊ฐ€ ๊ฒŒ์‹œํ•œ ์ด๋ฆ„์„ ์œ„ํ•œ ๊ฒƒ์ด๋‹ค. ์ฃผ์†Œ์—๋Š” ๋งž์ถœ ์ด๋ฆ„์ด ์—†์œผ๋ฏ€๋กœ, ์‚ฌ์„ค ์ฃผ์†Œ๋Š” ์—ฌ์ „ํžˆ provenance ๋กœ ์ž๊ธฐ ๊ธธ์„ ์–ป์–ด์•ผ ํ•œ๋‹ค.
  • http(s) ๋งŒ, ๊ทธ๋ฆฌ๊ณ  userinfo ๋กœ ํ˜ธ์ŠคํŠธ ๊ฒ€์‚ฌ๋ฅผ ์ง€๋‚˜์ณ ์ ‘๋ฏธ์‚ฌ๋ฅผ ๋ฐ€๋ฐ˜์ž…ํ•  ์ˆ˜ ์—†๋‹ค.

๊ทธ ๋Œ€๊ฐ€: ๊ทธ ์ ‘๋ฏธ์‚ฌ ์•„๋ž˜์˜ ํ˜ธ์ŠคํŠธ๋Š” admin ์ด ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ์–ด๋–ค URL ๋กœ๋„ ๋„๋‹ฌ ๊ฐ€๋Šฅํ•ด์ง€๊ณ , ๊ทธ๊ฒƒ์ด ๋ฐ”๋กœ ๊ฐ€๋“œ๊ฐ€ ํ‰์†Œ์— ์—†์• ๋Š” ๋Šฅ๋ ฅ์ด๋‹ค. ๊ทธ ์„œ๋ฒ„๋“ค์„ ์‹ค์ œ๋กœ ๋Œ๋ฆฌ๋Š” ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋งŒํผ ์ ‘๋ฏธ์‚ฌ๋ฅผ ๊ตฌ์ฒด์ ์œผ๋กœ ์œ ์ง€ํ•˜๋ผ.

managed ๋ฃจํ”„๋ฐฑ ์˜ˆ์™ธ

managed MCP ์„œ๋ฒ„(runtime: "managed")๋Š” ์ด ์•ฑ์ด ์ž๊ธฐ ํ˜ธ์ŠคํŠธ์—์„œ ์ง์ ‘ ๋„์šด ์ปจํ…Œ์ด๋„ˆ์ด๊ณ  127.0.0.1:<port> ๋กœ ๋‹ฟ๋Š”๋‹ค. ์šด์˜์ž๊ฐ€ ํƒ€์ดํ•‘ํ•œ ๊ฒƒ์ด๋ผ๋ฉด ๊ฐ€๋“œ๊ฐ€ ์ •ํ™•ํžˆ ๊ฑฐ๋ถ€ํ•  ์ฃผ์†Œ๋‹ค. ์‹ ๋ขฐ๋Š” ๋Œ€์‹  provenance ์— ๋†“์ธ๋‹ค. ํ”„๋กœ๋น„์ €๋„ˆ๊ฐ€ ํฌํŠธ๋ฅผ ๋ฐ”์ธ๋”ฉํ•œ ๋’ค ๊ทธ ์ฃผ์†Œ๋ฅผ ๊ธฐ๋กํ–ˆ๋‹ค.

isManagedLoopback(src/domain/mcp/types.ts)์ด ์ด ์šฐํšŒ๊ฐ€ ์ ์šฉ๋˜๋Š”์ง€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ์œ ์ผํ•œ ์ž๋ฆฌ์ด๊ณ , ์˜๋„์ ์œผ๋กœ ์ข๋‹ค. ํ•ญ๋ชฉ์€ managed ๋ฅผ ์ฃผ์žฅํ•ด์•ผ ํ•˜๊ณ  ๊ทธ๋ฆฌ๊ณ  ๋ฆฌํ„ฐ๋Ÿด ๋ฃจํ”„๋ฐฑ ์ฃผ์†Œ๋ฅผ ์ง€๋…€์•ผ ํ•œ๋‹ค:

  • 127.0.0.1 ๋กœ ํ•ด์„๋˜๋Š” ํ˜ธ์ŠคํŠธ๋ช…์€ ๊ฑฐ๋ถ€๋œ๋‹ค. ํ™•์ธ๊ณผ ์š”์ฒญ ์‚ฌ์ด์— ๋‹ค๋ฅธ ๊ณณ์œผ๋กœ ํ•ด์„๋  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฃจํ”„๋ฐฑ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” remote ํ•ญ๋ชฉ์€ ๊ฑฐ๋ถ€๋œ๋‹ค. ๊ทธ ์ฃผ์†Œ๋Š” ํƒ€์ดํ•‘๋œ ๊ฒƒ์ด๋‹ค.
  • ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋Š” managed ํ•ญ๋ชฉ์˜ URL ์„ ์˜ฎ๊ธฐ๊ธฐ๋ฅผ ๊ฑฐ๋ถ€ํ•œ๋‹ค.
  • ๋ผ์ดํ”„์‚ฌ์ดํด use case ๋Š” ํ”„๋กœ๋น„์ €๋„ˆ๊ฐ€ ๋ณด๊ณ ํ•˜๋”๋ผ๋„ ๋ฃจํ”„๋ฐฑ์ด ์•„๋‹Œ ์ฃผ์†Œ๋ฅผ ์ €์žฅํ•˜๊ธฐ๋ฅผ ๊ฑฐ๋ถ€ํ•˜๊ณ , ๊ทธ๊ฒƒ์ด ์ง€๋ชฉํ•œ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ •์ง€์‹œํ‚จ๋‹ค. ์ •์ง€๊นŒ์ง€ ์‹คํŒจํ•˜๋ฉด ์›๋ž˜ ๊ฑฐ๋ถ€์™€ cleanup ์‹คํŒจ๋ฅผ ํ•จ๊ป˜ ๋“œ๋Ÿฌ๋‚ด๋ฉฐ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ •๋ฆฌ๋๋‹ค๊ณ  ์ฃผ์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.

ํ”„๋กœ๋น„์ €๋„ˆ๋Š” ์ด๋ฏธ์ง€ ๋ ˆํผ๋Ÿฐ์Šค, ํฌํŠธ, ๊ทธ๋ฆฌ๊ณ  ์„ ํƒ์ ์ธ argv ๋ฐฐ์—ด ์„ ๋ฐ›๋Š”๋‹ค. ์…ธ ๋ช…๋ น์€ ๊ฒฐ์ฝ” ๋ฐ›์ง€ ์•Š๋Š”๋‹ค. ์œ ์ผํ•œ ๋Ÿฐํƒ€์ž„์ธ Docker ํ”„๋กœ๋น„์ €๋„ˆ(MANAGED_MCP_RUNTIME=docker, src/infrastructure/mcp/dockerProvisioner.ts)๋Š” argv ๋ฅผ execFile ๋กœ Docker CLI ์— ๋ฐฐ์—ด์งธ ๋„˜๊ธฐ๊ณ , ๊ตฌ์กฐ์ ์œผ๋กœ ์“ฐ์ด๋Š” ๊ฐ’. ์ด๋ฆ„(MANAGED_NAME), ์ด๋ฏธ์ง€ ๋ ˆํผ๋Ÿฐ์Šค, ํ™˜๊ฒฝ ํ‚คยท๊ฐ’, argv, endpoint path. ์€ src/domain/mcp/provisioner.ts์˜ ํŒจํ„ด์œผ๋กœ API ์ž…๋ ฅ๊ณผ Docker ์‹คํ–‰ ์–‘์ชฝ์—์„œ ๊ฒ€์‚ฌํ•œ๋‹ค. ํ•ญ๋ชฉ์„ ํŽธ์ง‘ํ•  ์ˆ˜ ์žˆ๋Š” ์šด์˜์ž๊ฐ€ ๊ทธ๊ฒƒ์œผ๋กœ ํ˜ธ์ŠคํŠธ์—์„œ ์ž„์˜ ์ฝ”๋“œ๋ฅผ ๋Œ๋ฆด ์ˆ˜๋Š” ์—†์–ด์•ผ ํ•œ๋‹ค. ํ˜ธ์ŠคํŠธ ํŒŒ์ผ ๊ฒฝ๋กœ๋Š” ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์ง€ ์•Š์œผ๋ฉฐ, ์ €์žฅ๋œ ํ™˜๊ฒฝ ๊ฐ’๋งŒ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋งŒ๋“  0600 ์ž„์‹œ env file ๋กœ ์ „๋‹ฌํ•œ๋‹ค.

์ปจํ…Œ์ด๋„ˆ๋Š” ๊ฐ๊ฐ ๋ฉ”๋ชจ๋ฆฌ์™€ memory+swap์„ ๋ชจ๋‘ 512MiB, CPU 1๊ฐœ, PID 256๊ฐœ๋กœ ์ œํ•œํ•˜๊ณ  Linux capability๋ฅผ ๋ชจ๋‘ ๋ฒ„๋ฆฌ๋ฉฐ no-new-privileges๋กœ ์‹คํ–‰๋œ๋‹ค. root filesystem์€ read-only์ด๊ณ  /tmp๋งŒ noexec,nosuid 64MiB tmpfs๋กœ ์“ธ ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ managed image๋Š” ์˜์† ๋กœ์ปฌ ์“ฐ๊ธฐ๋ฅผ ๊ฐ€์ •ํ•˜๋ฉด ์•ˆ ๋œ๋‹ค. ํ•œ ํ˜ธ์ŠคํŠธ์—๋Š” managed ํ•ญ๋ชฉ์„ 8๊ฐœ๊นŒ์ง€๋งŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ณ  ์„œ๋กœ ๋‹ค๋ฅธ ์ด๋ฆ„์˜ ๋™์‹œ ์ƒ์„ฑ๋„ count-then-create ๊ตฌ๊ฐ„์—์„œ ์ง๋ ฌํ™”๋œ๋‹ค. ๊ธฐ์กด ํ•ญ๋ชฉ์˜ restartยทreconcile์€ ์ด ์ƒํ•œ ๋•Œ๋ฌธ์— ๋ง‰ํžˆ์ง€ ์•Š๋Š”๋‹ค.

MCP ์„œ๋ฒ„๊ฐ€ ํ˜ธ์ถœ์ž์— ๋Œ€ํ•ด ๋“ฃ๋Š” ๊ฒƒ

application/mcpMetadataHeaders.ts๊ฐ€ ์ €์žฅ๋œ ์˜ˆ์•ฝ header๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ํ™•์ธํ•œ ์‹ ์›์„ ๋ถ™์ธ๋‹ค. ๋‹ค๋ฅธ ๋Œ€์†Œ๋ฌธ์ž ํ‘œ๊ธฐ๋„ ๊ฐ™์€ ์ด๋ฆ„์œผ๋กœ ์ทจ๊ธ‰ํ•˜๋ฉฐ OAuth ๊ฐ€์šฉ์„ฑ ๊ฒ€์‚ฌ ์ „์— ์ œ๊ฑฐํ•œ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ •์  header๋ฅผ ์ €์žฅํ•ด ๋‹ค๋ฅธ ์‚ฌ๋žŒยทAgentยท๋Œ€ํ™”๋ฅผ ์‚ฌ์นญํ•  ์ˆ˜ ์—†๋‹ค.

Header ์˜๋ฏธยท๋ฒ”์œ„
X-Tenant-Id ์‹คํ–‰ Agent ์ด๋ฆ„. Agent๋ณ„ ๋„๊ตฌ ๋ชฉ๋ก๊ณผ discovery cache๋ฅผ ๊ตฌ๋ถ„ํ•œ๋‹ค
X-User-Email ํ‘œ๋ฉด์ด ๊ฒ€์ฆํ•œ Studio ์‚ฌ์šฉ์ž์˜ ํ˜„์žฌ ์ด๋ฉ”์ผ. ๋ฉ”์‹ ์ €๋Š” ์—ฐ๊ฒฐ ์‚ฌ์šฉ์ž, Webhook์€ ํ† ํฐ ๋ฐœ๊ธ‰์ž, Schedule์€ ๋“ฑ๋ก์ž ID๋กœ ํ™•์ธํ•˜๋ฉฐ ์‹ ์› cache key์— ํฌํ•จํ•œ๋‹ค
X-Conversation-Id ๋Œ€ํ™” ์ฃผ์†Œ. ์š”์ฒญ์˜ context header์ด๋ฉฐ discovery cache key์— ํฌํ•จํ•˜์ง€ ์•Š๋Š”๋‹ค

์ด๋ฉ”์ผ์€ MCP discovery๋ถ€ํ„ฐ ํ‰๋ฌธ์œผ๋กœ ์ „์†กํ•˜๋ฉฐ PII ํ•„ํ„ฐ๊ฐ€ ๊ฐ€๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. ์„œ๋ฒ„ ๋“ฑ๋ก์€ ์‚ฌ์šฉ์ž ์ด๋ฉ”์ผ๊ณผ ๋ณต์›๋œ ๋„๊ตฌ ์ธ์ž ๊ณต๊ฐœ๋ฅผ ํฌํ•จํ•˜๋Š” ์‹ ๋ขฐ ๊ฒฐ์ •์ด๋‹ค. ์ด header๋งŒ์œผ๋กœ ์ธ์ฆ๋˜์ง€ ์•Š์œผ๋ฉฐ ์„œ๋ฒ„๋Š” ๋ณ„๋„ BearerยทOAuth grant์™€ ํ•จ๊ป˜ ์œ„์ž„ ์‹ ์›์„ ๊ฒ€์ฆํ•ด์•ผ ํ•œ๋‹ค.

registry Test์™€ Agent ๋„๊ตฌ ์กฐํšŒ๋Š” ์š”์ฒญ ์‚ฌ์šฉ์ž email์„ ์ „์†กํ•œ๋‹ค. ์นดํƒˆ๋กœ๊ทธยทmanaged health probe์—๋Š” ์‚ฌ์šฉ์žยท๋Œ€ํ™”๊ฐ€ ์—†๋‹ค. Agent ๋„๊ตฌ ์กฐํšŒ๋Š” OAuth์™€ override๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ tenant header๋Š” ๋ณด๋‚ด์ง€ ์•Š๋Š” ํ˜„์žฌ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ tenant๋งˆ๋‹ค ๋„๊ตฌ ๋ชฉ๋ก์ด ๋‹ค๋ฅธ ์„œ๋ฒ„์—์„œ๋Š” probe์™€ ๋Ÿฐ์˜ ๋ชฉ๋ก์ด ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋‹ค.

API ๋Œ€ํ™” ์ฃผ์†Œ์˜ caller ๋ถ€๋ถ„์€ ๋ฐฐํฌ ํ‚ค๋กœ ๋งŒ๋“  digest๋‹ค. ์ด๋ฉ”์ผ์˜ ํ‰๋ฌธ hash๊ฐ€ ์•„๋‹ˆ์ง€๋งŒ ๋™์ผ ์‚ฌ์šฉ์ž๋ฅผ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€๋ช…์ด์ง€ ์ต๋ช…ํ™”๋Š” ์•„๋‹ˆ๋‹ค. ๋ฉ”์‹ ์ €ยทChat์˜ ์ฃผ์†Œ ํ˜•์‹์€ ๊ด€์ธก์„ฑ ์„ค๊ณ„๋ฅผ ๋”ฐ๋ฅธ๋‹ค. firing์ด๋‚˜ ๋Œ€ํ™” ID ์—†๋Š” ์š”์ฒญ์€ ๋Œ€ํ™” header๋ฅผ ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค.

๋ชจ๋ธ์ด ๊ณ ๋ฅธ URL

FetchUrl์€ ๋ชจ๋ธ์ด ์ง€๋ชฉํ•œ ์ฃผ์†Œ๋ฅผ src/infrastructure/net/httpResource.ts๋กœ ์š”์ฒญํ•œ๋‹ค. registry์˜ ๋“ฑ๋ก ์‹œ ๊ฒ€์ฆ์„ ๊ฑฐ์น˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํ˜ธ์ถœ ์ž์ฒด์˜ URLยทDNSยทredirectยทbyte ์ œํ•œ์ด ๊ฒฝ๊ณ„๋‹ค. ๊ฐ™์€ ๊ณต๊ฐœ fetch ๊ฒฝ๊ณ„๋Š” Teams ์ฒจ๋ถ€์™€ source ํŒŒ์ผ ์ฃผ์†Œ์—๋„ ์ ์šฉํ•˜์ง€๋งŒ ๊ฐ ๊ฒฝ๋กœ์˜ credentialยท ๋‚ด๋ถ€ ํ˜ธ์ŠคํŠธ ์˜ˆ์™ธ๋Š” ๋ณ„๋„ ์ •์ฑ…์„ ๋”ฐ๋ฅธ๋‹ค.

  • MCP ์˜ ๋‚ด๋ถ€ ํ˜ธ์ŠคํŠธ ์˜ˆ์™ธ๋Š” ๊ฒฐ์ฝ” ์ฐธ์กฐํ•˜์ง€ ์•Š๋Š”๋‹ค. MCP_INTERNAL_HOST_SUFFIXES ๋Š” ์ด ์•ฑ์ด ์ž๊ธฐ ํด๋Ÿฌ์Šคํ„ฐ์˜ MCP ์„œ๋น„์Šค์— ๋‹ฟ์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋ ค๊ณ  ์กด์žฌํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ ๊ทธ๊ฒƒ์„ ์กด์ค‘ํ•˜๋ฉด ํ”„๋กฌํ”„ํŠธ ์ธ์ ์…˜ ํ•˜๋‚˜๊ฐ€ http://mcp-argocd.agent-mcps.svc.cluster.local/ ์„ ์ฝ๋Š” ์ผ๋กœ ๋ฐ”๋€๋‹ค. ์–ด๋Œ‘ํ„ฐ๊ฐ€ skipsUrlGuard ๋ฅผ import ํ•˜๊ฑฐ๋‚˜ ๊ทธ ๋ชฉ๋กยทprocess.envยทlib/config ์— ๋‹ฟ๊ธฐ๋งŒ ํ•ด๋„ tests/architecture.test.ts ๊ฐ€ ์‹คํŒจํ•œ๋‹ค.
  • ์ž๊ธฐ ๋ชฉ๋ก์€ ๋”ฐ๋กœ ์žˆ๋‹ค. URL_FETCH_INTERNAL_HOST_SUFFIXES. ์‚ฌ๋‚ด ์œ„ํ‚ค๋‚˜ ๋‚ด๋ถ€ API ๊ฐ€ ๊ตฌ์กฐ์ƒ ์‚ฌ์„ค ์ฃผ์†Œ์ธ ์„ค์น˜ํ˜• ๋„คํŠธ์›Œํฌ๋ฅผ ์œ„ํ•œ ๊ฒƒ์ด๊ณ , composition root ๊ฐ€ ์ฃผ์ž…ํ•˜๋ฉฐ ๋ฐฐํฌ๋กœ๋งŒ ๋„“์–ด์ง„๋‹ค. ๊ทธ ์ ‘๋ฏธ์‚ฌ ์•„๋ž˜์˜ ํ˜ธ์ŠคํŠธ(isDeclaredInternalHost, ๊ฐ™์€ ์ˆ ์–ด, ๊ฐ™์€ ์ฃผ์˜: ๋ ˆ์ด๋ธ” ๊ฒฝ๊ณ„, ๋‹จ์ผ ๋ ˆ์ด๋ธ” ๊ฑฐ๋ถ€, IP ๋ฆฌํ„ฐ๋Ÿด ๊ฑฐ๋ถ€, http(s) ๋งŒ)๋Š” ์ฃผ์†Œ ๊ฐ€๋“œ ์—†์ด ์š”์ฒญ๋˜์ง€๋งŒ, ๋‚˜๋จธ์ง€๋Š” ๊ทธ๋Œ€๋กœ๋‹ค: ๊ฐ™์€ 15์ดˆ ํƒ€์ž„์•„์›ƒ, ๊ฐ™์€ ๋‘ ํ—ค๋”, ๊ฐ™์€ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์ƒํ•œ(5)์ด๊ณ , ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ์ถ”์ข… ์—†์ด ํ™‰๋งˆ๋‹ค ํ™•์ธ๋˜์–ด ์„ ์–ธ๋œ ์ง‘ํ•ฉ์„ ๋ฒ—์–ด๋‚˜๊ฑฐ๋‚˜ ์ถœ๋ฐœ origin ์„ ๋ฒ—์–ด๋‚˜๋ฉด ๊ฑฐ๋ถ€๋œ๋‹ค. ์˜ˆ์™ธ๊ฐ€ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ํ•œ ๋ฒˆ์œผ๋กœ ๋„“์–ด์ ธ์„œ๋Š” ์•ˆ ๋˜๋Š” ์œ ์ผํ•œ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋นˆ ๋ชฉ๋ก(๊ธฐ๋ณธ)์€ ๋ชจ๋“  ๋ชจ๋ธ ์„ ํƒ URL ์ด ๊ฐ€๋“œ๋ฅผ ๋งˆ์ฃผํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค. ๊ทธ ๋Œ€๊ฐ€๋Š” MCP ๋ชฉ๋ก์˜ ๊ฒƒ๊ณผ ๊ฐ™๋‹ค: ์ ‘๋ฏธ์‚ฌ ์•„๋ž˜์˜ ๋ชจ๋“  ํŽ˜์ด์ง€๊ฐ€ ํ”„๋กฌํ”„ํŠธ ์ธ์ ์…˜ ํ•˜๋‚˜๋กœ ์ฝํžŒ๋‹ค. ์ •๋ง๋กœ ๋ชจ๋ธ์ด ์ฝ์–ด๋„ ๋˜๋Š” ์กด๋งŒํผ ์ข๊ฒŒ ์„ ์–ธํ•˜๋ผ.
  • ์•„๋ฌด๊ฒƒ๋„ ์ธ์ฆํ•˜์ง€ ์•Š๋Š”๋‹ค. ํ…Œ๋„ŒํŠธ ํ—ค๋” ์—†์Œ, MCP OAuth token ์—†์Œ, Slack token ์—†์Œ, ํ˜ธ์ถœ์ž ํ—ค๋” ์ „๋‹ฌ ์—†์Œ. ํ•ญ์ƒ GET ์ด๊ณ  ๋ณธ๋ฌธ์€ ๊ฒฐ์ฝ” ์—†๋‹ค. ๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋Š” ์• ์ดˆ์— ๋ถ™์ง€ ์•Š์€ ๊ฒƒ์„ ์ „๋‹ฌํ•  ์ˆ˜ ์—†๊ณ . fetchPublicUrl ์ด ์–ด์ฐจํ”ผ ๊ทธ๊ฒƒ์„ ๊ฑฐ๋ถ€ํ•œ๋‹ค.
  • ๊ฑฐ๋ถ€๋Š” ์ผ๋ฐ˜ํ™”๋œ๋‹ค. PublicFetchError ๋Š” ์ž๊ธฐ๊ฐ€ ๊ฑฐ๋ถ€ํ•œ ํ˜ธ์ŠคํŠธ๋ฅผ ์ง€๋ชฉํ•œ๋‹ค. ๊ทธ๊ฒƒ์„ ๋ชจ๋ธ์—๊ฒŒ ๊ฑด๋„ค๋ฉด ๋„๊ตฌ๊ฐ€ ์–ด๋–ค ๋‚ด๋ถ€ ์ด๋ฆ„์ด ์กด์žฌํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์˜ค๋ผํด์ด ๋œ๋‹ค. ํ˜ธ์ถœ์ž๋Š” "๊ทธ ์ฃผ์†Œ๋Š” ์—ฌ๊ธฐ์„œ ๋„๋‹ฌํ•  ์ˆ˜ ์—†๋‹ค"๋ฅผ ๋ฐ›๊ณ , ์ƒ์„ธ๋Š” origin ๋งŒ ๋กœ๊ทธ๋กœ ๊ฐ„๋‹ค. URL ์ž์ฒด๊ฐ€ ์ž๊ฒฉ ์ฆ๋ช…์ธ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.
  • ๋Ÿฐ๋‹น ์ œํ•œ๋œ๋‹ค. MAX_URL_FETCHES_PER_RUN(20)์€ ์š”์ฒญ์˜ ๊ฐœ์ˆ˜ ๋ฅผ ์ œํ•œํ•˜๋Š”๋ฐ, ๋‹ค๋ฅธ ์–ด๋–ค ์˜ˆ์‚ฐ๋„ ๊ทธ๋ ‡๊ฒŒ ํ•˜์ง€ ์•Š๋Š”๋‹ค. "๋งŽ์€ ์š”์ฒญ, ์ „๋ถ€ ์‹คํŒจ"๋Š” ๋„คํŠธ์›Œํฌ ์Šค์œ•์ด ์ทจํ•˜๋Š” ๋ชจ์–‘์ด๋‹ค.
  • ๊ธฐ๋ณธ์€ ๊บผ์ง. Agent๊ฐ€ parameters.urlFetch ๋กœ ์˜ตํŠธ์ธํ•œ๋‹ค. ๊ทธ capability ๋Š” ์ฃผ์ž…๋œ ์˜์กด์„ฑ์—์„œ ํŒŒ์ƒ๋˜๋ฏ€๋กœ Playground ํ”„๋ฆฌ๋ทฐ์™€ ๋Ÿฐ์ด ์„œ๋กœ ์–ด๊ธ‹๋‚  ์ˆ˜ ์—†๋‹ค.

์ด๊ฒƒ์ด ๋ง‰์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ. ๊ณต๊ฐœ๋ผ ์žˆ์ง€๋งŒ ๋ฏผ๊ฐํ•œ ํ˜ธ์ŠคํŠธ. ํŒŒ๋“œ์˜ ์ด๊ทธ๋ ˆ์Šค ์ฃผ์†Œ๋ฅผ ์‹ ๋ขฐํ•˜๋Š” IP ํ—ˆ์šฉ๋ชฉ๋ก ๊ธฐ๋ฐ˜ SaaS. ๋Š” ๊ฐ€๋“œ๋ฅผ ํ†ต๊ณผํ•œ๋‹ค. ์œ ์ถœ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋‹ค. https://attacker.example/?leak=โ€ฆ ์„ ์š”์ฒญํ•˜๋„๋ก ์„ค๋“๋‹นํ•œ ๋ชจ๋ธ์€ ํ‰๋ฒ”ํ•œ ์•„์›ƒ๋ฐ”์šด๋“œ ์š”์ฒญ์„ ํ•˜๋Š” ๊ฒƒ์ด๊ณ , PII ํ•„ํ„ฐ๋ง์€ ๋„์›€์ด ๋˜์ง€ ์•Š๋Š”๋‹ค. fetch ์—๋Š” ๋ณต์›๋œ ์ธ์ž๊ฐ€ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค(๋งˆ์Šคํ‚น๋œ URL ์€ ํ•ด์„๋˜์ง€ ์•Š๋Š”๋‹ค). ์ด๊ฒƒ์€ ์•„๋ž˜์—์„œ MCP ๋„๊ตฌ ์ธ์ž์— ๋Œ€ํ•ด ์ด๋ฏธ ๋งํ•œ ๊ฒƒ๊ณผ ๊ฐ™์€ ํ•œ๊ณ„๋‹ค. ์ฐจ์ด๋Š” URL ์ด ๋งˆ์ฐฐ์ด ๋” ์ ์€ ํ†ต๋กœ๋ผ๋Š” ์ ์ด๋‹ค.

์ด HTTP ์š”์ฒญ์€ ์•ฑ ํ”„๋กœ์„ธ์Šค์—์„œ ์ˆ˜ํ–‰ํ•œ๋‹ค. SSRF ๊ฐ€๋“œ์™€ ๋ณ„๊ฐœ๋กœ ๋ฐฐํฌ์˜ egress ์ •์ฑ…์„ ์ ์šฉํ•˜๊ณ , ํ—ˆ์šฉํ•œ ๋‚ด๋ถ€ ํŽ˜์ด์ง€์™€ IP ๊ธฐ๋ฐ˜ ์‹ ๋ขฐ ์„œ๋น„์Šค๊ฐ€ ๋ชจ๋ธ์— ๋…ธ์ถœ๋  ์ˆ˜ ์žˆ์Œ์„ ๊ณ ๋ คํ•œ๋‹ค.

MCP OAuth

๊ด€๋ฆฌ์ž๋Š” registry์— OAuth ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์™€ ์„ ํƒ์  ๊ณต์œ  client๋ฅผ ๋“ฑ๋กํ•˜๊ณ , connection์€ Studio ์‚ฌ์šฉ์ž IDยทMCP ์„œ๋ฒ„๋ณ„๋กœ ๋ณธ์ธ์˜ grant๋ฅผ ๋ณด๊ด€ํ•˜๋ฉฐ Agent ๊ฐ„์— ์žฌ์‚ฌ์šฉํ•œ๋‹ค. ์‹คํ–‰ ๊ฒฝ๋กœ๋Š” well-known ๋ฌธ์„œ๋ฅผ ๋‹ค์‹œ ์ฝ์ง€ ์•Š๊ณ  ์ €์žฅ๋œ ๊ณ„์•ฝ์œผ๋กœ token์„ ํ•ด์„ํ•œ๋‹ค. ๋ฐœ๊ฒฌยท์—ฐ๊ฒฐยทcallback์˜ HTTP ํ˜•ํƒœ๋Š” API, refresh ์ˆ˜๋ช…์€ MCP ์„ค๊ณ„๋ฅผ ๋”ฐ๋ฅธ๋‹ค.

๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์™€ ์ฃผ์†Œ

protected-resource ๋ฌธ์„œ๋Š” MCP ์ฃผ์†Œ์˜ 401 challenge๊ฐ€ ์ง€์ •ํ•œ URL์„ ์šฐ์„ ํ•œ๋‹ค. challenge๊ฐ€ ์—†์œผ๋ฉด well-known ํ›„๋ณด๋ฅผ ์ฝ๋Š”๋‹ค. resource ๋ฌธ์„œ๋Š” ๊ทธ MCP ํ•ญ๋ชฉ์˜ ๋‚ด๋ถ€ ํ˜ธ์ŠคํŠธ ์˜ˆ์™ธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ฌธ์„œ๊ฐ€ ์ง€๋ชฉํ•œ authorization server๊นŒ์ง€ ๊ฐ™์€ ์˜ˆ์™ธ๋ฅผ ์ฃผ์ง€๋Š” ์•Š๋Š”๋‹ค. authorizationยทtokenยทregistration endpoint๋Š” HTTPS์™€ URL ์ •์ฑ…์œผ๋กœ ๊ฒ€์‚ฌํ•œ๋‹ค. ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์กฐํšŒ ์‹คํŒจ ๋ฉ”์‹œ์ง€์—๋Š” ํ›„๋ณด ์ฃผ์†Œ์˜ userinfoยทqueryยทfragment์™€ ์ „์†ก ์˜ค๋ฅ˜ ์›๋ฌธ์„ ์‹ฃ์ง€ ์•Š๋Š”๋‹ค.

resource identifier๋Š” ๋ฌธ์„œ๋ฅผ ์ฐพ์€ ๋Œ€์ƒ๊ณผ ์ผ์น˜ํ•ด์•ผ ํ•œ๋‹ค. ์ œํ•œ๋œ ์˜ˆ์™ธ๋Š” https://mcp.slack.com/mcp๊ฐ€ ๊ณต์‹ well-known ๋ฌธ์„œ๋ฅผ ์ง€๋ชฉํ•  ๋•Œ ๋ฌธ์„œ์˜ resource: https://mcp.slack.com์„ ํ—ˆ์šฉํ•˜๋Š” ์กฐํ•ฉ์ด๋‹ค. ๋‹ค๋ฅธ originยท๊ฒฝ๋กœ ์กฐํ•ฉ์œผ๋กœ ์ผ๋ฐ˜ํ™”ํ•˜์ง€ ์•Š๋Š”๋‹ค.

authorization server๋Š” RFC 8414 path-inserted, OpenID path-inserted, OpenID path-appended ์ˆœ์„œ๋กœ ๋ฐœ๊ฒฌํ•œ๋‹ค. ๊ฒฝ๋กœ๋ฅผ ๊ฐ€์ง„ issuer์—์„œ root ๋ฌธ์„œ๋กœ ํ›„ํ‡ดํ•˜์ง€ ์•Š์œผ๋ฉฐ ๋ฌธ์„œ์˜ issuer๋ฅผ ๊ฒ€์ฆํ•œ๋‹ค. Google discovery๋Š” ๊ด‘๊ณ  ์ฃผ์†Œ https://accounts.google.com/์— ๋Œ€ํ•ด ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์˜ https://accounts.google.com๋งŒ ์ถ”๊ฐ€๋กœ ํ—ˆ์šฉํ•œ๋‹ค. callback์˜ iss์—๋Š” ์ด ์˜ˆ์™ธ๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. Entra์˜ common endpoint์ฒ˜๋Ÿผ ๋ฌธ์„œ๊ฐ€ ๋‹ค๋ฅธ tenant issuer๋ฅผ ๋Œ๋ ค์ฃผ๋ฉด ์ผ์น˜ ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•˜์ง€ ๋ชปํ•˜๋ฏ€๋กœ resource๊ฐ€ ์‚ฌ์šฉํ•  tenant issuer๋ฅผ ์ง์ ‘ ๊ด‘๊ณ ํ•ด์•ผ ํ•œ๋‹ค.

registry URL ๋ณ€๊ฒฝ์€ ์ด์ „ auth๋ฅผ ํ๊ธฐํ•œ๋‹ค. ์ƒˆ ์ฃผ์†Œ์˜ Discover๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ ์ด์ „ ์—ฐ๊ฒฐ์„ ๊ฐ™์€ ์ด๋ฆ„์ด๋ผ๋Š” ์ด์œ ๋กœ ์žฌ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. OAuth ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์™€ ๊ณต์œ  ์•ฑ ์ €์žฅ์€ ์ฝ์€ auth์— ๋Œ€ํ•œ ์กฐ๊ฑด๋ถ€ ์“ฐ๊ธฐ๋กœ ๋™์‹œ ๋ณ€๊ฒฝ์„ ๋ณดํ˜ธํ•œ๋‹ค.

Clientยท์ธ๊ฐ€ยท์ฝœ๋ฐฑ

๊ฒ€์‚ฌ ๊ตฌํ˜„ ๊ณ„์•ฝ
client ์„ ํƒ ๊ธฐ์กด credential โ†’ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Client ID Metadata Document โ†’ dynamic registration โ†’ ์„ค์ • ์˜ค๋ฅ˜
PKCE S256 ํ•„์ˆ˜. ๊ด‘๊ณ ํ•˜์ง€ ์•Š๋Š” ์„œ๋ฒ„๋Š” ๊ธฐ๋ณธ ๊ฑฐ์ ˆํ•˜๋ฉฐ ๋ฐฐํฌ์˜ MCP_OAUTH_ALLOW_UNADVERTISED_PKCE๋งŒ ์˜ˆ์™ธ๋ฅผ ํ—ˆ์šฉ
state ์ผํšŒ์šฉยท10๋ถ„ ๋งŒ๋ฃŒ. ์‚ฌ์šฉ์ž IDยทemail, verifier, client, issuer, resource์™€ redirect URI๋ฅผ ํ•จ๊ป˜ ๋ณด๊ด€
redirect URI ์„œ๋ฒ„ ๊ณต๊ฐœ base์˜ ๊ณ ์ • callback. Tools์˜ ์ˆ˜๋™ ๊ฐ’๋„ ๊ฐ™์€ ์ฃผ์†Œ์—ฌ์•ผ ํ•จ
resource authorization๊ณผ token ์š”์ฒญ์— ๋Œ€์ƒ resource๋ฅผ ํฌํ•จ
callback issuer code ๊ตํ™˜ ์ „์— pending state์˜ issuer์™€ ๋ฌธ์ž ๊ทธ๋Œ€๋กœ ๋น„๊ต. provider๊ฐ€ ์ง€์›์„ ๊ด‘๊ณ ํ–ˆ๋Š”๋ฐ iss๊ฐ€ ์—†์œผ๋ฉด ๊ฑฐ์ ˆ
์˜ค๋ฅ˜ callback issuer๋ฅผ ๊ฒ€์ฆํ•  ์ˆ˜ ์—†๋Š” ์‘๋‹ต์˜ error_description์„ ๊ทธ๋Œ€๋กœ ์ „๋‹ฌํ•˜์ง€ ์•Š์Œ
callback ๊ถŒํ•œ ์‹œ์ž‘ํ•œ ์‚ฌ์šฉ์ž IDยทemail, ํ˜„์žฌ ๊ณ„์ •ยทmember ๋“ฑ๊ธ‰ยทAgent ์ ‘๊ทผ๊ณผ clientยทresource๋ฅผ ๋‹ค์‹œ ํ™•์ธ
token endpoint ์ธ์ฆ ๋“ฑ๋กํ•œ ์ธ์ฆ ๋ฐฉ์‹์„ ์—ฐ๊ฒฐ์— ์ €์žฅยท์‚ฌ์šฉ. basic์€ client ID์™€ secret์„ form encoding ํ›„ Base64. secret ์—†๋Š” client๋Š” none
๋™์  ๋“ฑ๋ก application_type: "web"์„ ๋ช…์‹œํ•˜๊ณ  token ์š”์ฒญ๊ณผ ๊ฐ™์€ ์ธ์ฆ ๋ฐฉ์‹์œผ๋กœ ๋“ฑ๋ก

๋“ฑ๋ก client credential์€ ๋ฐœ๊ธ‰ issuer์—, token์€ ๋ฐœ๊ธ‰ ๋‹น์‹œ resource์— ๋ฌถ์ธ๋‹ค. ์ด ํ•„๋“œ๊ฐ€ ์—†๋Š” ์—ฐ๊ฒฐ์ด๋‚˜ pending state๋ฅผ ํ˜„์žฌ registry์— ์†ํ•œ๋‹ค๊ณ  ์ถ”์ธกํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ƒˆ ์—ฐ๊ฒฐ์ด ํ•„์š”ํ•˜๋‹ค๊ณ  ์•Œ๋ฆฌ๊ณ  grant ์‚ฌ์šฉยท์ธ๊ฐ€ ์™„๋ฃŒ๋ฅผ ๊ฑฐ์ ˆํ•œ๋‹ค.

๊ณต์œ  ์•ฑ์€ clientFromRegistry์™€ Client ID๋ฅผ ๊ธฐ๋กํ•˜๊ณ  code ๊ตํ™˜ยทrefresh ๋•Œ ํ˜„์žฌ Secret์„ ์ฝ๋Š”๋‹ค. Secret ํšŒ์ „์€ ๊ธฐ์กด grant์— ๋ฐ˜์˜ํ•˜์ง€๋งŒ Client ID์˜ ๊ต์ฒดยท์ œ๊ฑฐ๋Š” ๊ธฐ์กด grant๋ฅผ ์ฐจ๋‹จํ•œ๋‹ค. ๊ฐœ๋ณ„ ๋™์  ๋“ฑ๋ก Secret์€ ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ connection์— ๋ณด๊ด€ํ•œ๋‹ค. ์—ฐ๊ฒฐ ํ™”๋ฉด์˜ ์„œ๋น„์Šค ๊ณ„์ •์€ ์ธ์ฆยท์ ‘๊ทผ ํŒ์ •์— ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ณต์‹ OAuth endpoint ์กฐํ•ฉ์˜ GitHubยทGoogle ๊ธฐ๋ณธ ์กฐํšŒ๋Š” ํ•ด๋‹น ์ œ๊ณต์ž์˜ ๊ณ ์ • ๊ณ„์ • API๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์ผ๋ฐ˜ OIDC๋Š” ๊ฒ€์ฆํ•œ discovery์˜ UserInfo endpoint๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, ๊ด€๋ฆฌ์ž ์ง€์ • HTTP ๊ณ„์•ฝ๋„ HTTPSยทURL ์ •์ฑ…์œผ๋กœ ์ €์žฅ๊ณผ dispatch ๋•Œ ๊ฒ€์ฆํ•œ๋‹ค. NotionยทPlaud token๊ณผ ์ˆ˜๋™ MCP ๊ณ„์•ฝ์€ ๋ฐœ๊ธ‰๋œ resource์— ํ•ด๋‹นํ•˜๋Š” ํ˜„์žฌ registry URL์˜ ํ˜„์žฌ ์‚ฌ์šฉ์ž ์กฐํšŒ ๋„๊ตฌ์—๋งŒ ์‚ฌ์šฉํ•œ๋‹ค. ์ˆ˜๋™ MCP ๊ณ„์•ฝ์€ ์„œ๋ฒ„๊ฐ€ readOnlyHint: true๋กœ ์„ ์–ธํ•œ ๋„๊ตฌ๋งŒ ํ˜ธ์ถœํ•œ๋‹ค. lookup ์„ค์ •์€ ๊ด€๋ฆฌ์ž ์ „์šฉ์ด๋ฉฐ ์ž๋™ ์„ ํƒ์œผ๋กœ ์ž„์˜์˜ ๋„๊ตฌ๋‚˜ ์“ฐ๊ธฐ ์ž‘์—…์„ ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ชจ๋“  ์š”์ฒญ์€ ๊ณต๊ฐœ URL ๊ฐ€๋“œ์™€ ๋™์ผ origin redirect ์ •์ฑ…์„ ๋”ฐ๋ฅธ๋‹ค. ๊ณ„์ • ์กฐํšŒ ์‘๋‹ต์€ ์„ ํƒํ•œ ๊ณ„์ • label๋งŒ ๋ณด๊ด€ํ•˜๊ณ  ์˜ค๋ฅ˜ ์›๋ฌธ์ด๋‚˜ token์€ ํ™”๋ฉดยท๋กœ๊ทธ์— ์ „๋‹ฌํ•˜์ง€ ์•Š๋Š”๋‹ค. Studio ์‚ฌ์šฉ์ž ์ด๋ฉ”์ผ์„ ์„œ๋น„์Šค ๊ณ„์ •์œผ๋กœ ์ถ”์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ˆ˜๋™ JSON Pointer์™€ ๋„๊ตฌ ์ธ์ž์— tokenยทpassword ๋“ฑ ์ž๊ฒฉ ์ฆ๋ช… ํ•„๋“œ๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š์œผ๋ฉฐ, ์„œ๋ฒ„ ์‘๋‹ต์ด ์กฐํšŒ token์„ label์— ํฌํ•จํ•ด ๋ฐ˜์‚ฌํ•˜๋”๋ผ๋„ ๊ณ„์ • ํ‘œ์‹œยท์ €์žฅ์œผ๋กœ ๋„˜์–ด๊ฐ€์ง€ ์•Š๋Š”๋‹ค. Client IDยทSecret ์ˆ˜๋™ ์ž…๋ ฅ์€ ๊ด€๋ฆฌ์ž ๊ณต์šฉ ์•ฑ ์„ค์ •์—๋งŒ ์ œ๊ณตํ•œ๋‹ค. ๊ฐœ์ธ ์—ฐ๊ฒฐ ๋ชฉ๋ก์€ Client Secret๊ณผ access/refresh token์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š”๋‹ค. issuerยทresource๊ฐ€ ๋ฐ”๋€ grant๋Š” ์‹คํ–‰์—์„œ ๊ฑฐ์ ˆํ•˜๋ฉฐ ์‚ฌ์šฉ์ž๋Š” ํ˜„์žฌ ์„œ๋ฒ„ ์„ค์ •์œผ๋กœ ๋‹ค์‹œ ์ธ๊ฐ€ํ•œ๋‹ค.

๊ณต๊ฐœ Client ID ๋ฌธ์„œ

/api/mcps/oauth/client-metadata๋Š” authorization server๊ฐ€ ์ฟ ํ‚ค ์—†์ด ๊ฐ€์ ธ๊ฐ€๋Š” ๋ฌธ์„œ๋‹ค. secret์€ ์—†๊ณ  client ์ด๋ฆ„ยทclient ID URLยท๊ณ ์ • redirect URI๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ์š”์ฒญ Host ๋Œ€์‹  ์„ค์ •๋œ ๊ณต๊ฐœ base๋กœ ์ฃผ์†Œ๋ฅผ ๋งŒ๋“ ๋‹ค. ์ด URL์„ ์‹ค์ œ client ID๋กœ ์„ ํƒํ•˜๋Š” ์ธ๊ฐ€ ์ค€๋น„ ๋‹จ๊ณ„์—์„œ HTTPSยท๊ณต๊ฐœ ๋„๋‹ฌ ๊ฐ€๋Šฅ ์กฐ๊ฑด์„ ํ™•์ธํ•œ๋‹ค.

๋ฌธ์„œ๋Š” Agent ์ด๋ฆ„์ด๋‚˜ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š๋Š”๋‹ค. ์‹ค์ œ callback์€ ์‹œ์ž‘ํ•œ ์‚ฌ์šฉ์ž์˜ IDยทstateยท์—ฐ๊ฒฐยทํ˜„์žฌ Agent ์ ‘๊ทผ ๊ถŒํ•œ์„ ์š”๊ตฌํ•œ๋‹ค. ๊ณต๊ฐœ base๊ฐ€ ์—†๊ฑฐ๋‚˜ provider๊ฐ€ ๊ฐ€์ ธ๊ฐˆ ์ˆ˜ ์—†๋Š” ์ฃผ์†Œ๋ฉด metadata ๋ฐฉ์‹ ๋Œ€์‹  ์ง€์›๋˜๋Š” ๋“ฑ๋ก ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์„ค์ • ์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

metadata client๋Š” ์ž๊ธฐ ํ˜ธ์ŠคํŒ… URL์ด๋ฏ€๋กœ ๋“ฑ๋ก client์™€ ๋‹ฌ๋ฆฌ issuer ๋ณ€๊ฒฝ ๋•Œ client ID ์ž์ฒด๋ฅผ ํ๊ธฐํ•  ํ•„์š”๋Š” ์—†๋‹ค. ๋‹ค๋งŒ grant์˜ issuerยทresource ๊ฒ€์‚ฌ๋Š” ๊ทธ๋Œ€๋กœ ์ ์šฉํ•œ๋‹ค. ๋ฐฐํฌ์˜ ๊ณต๊ฐœ base๊ฐ€ ๋‹ฌ๋ผ์ง€๋ฉด ์ƒˆ ๋ฌธ์„œ ์ฃผ์†Œ๋กœ ๊ฐฑ์‹ ํ•œ๋‹ค.

๊ฐฑ์‹ ๊ณผ dispatch

refresh ๊ฒฐ๊ณผ๋Š” connection revision์„ ๋น„๊ตํ•ด ์ €์žฅํ•œ๋‹ค. refresh token์„ ์ƒ๋žตํ•œ ์‘๋‹ต์€ ์ด์ „ ๊ฐ’์„ ์œ ์ง€ํ•˜๊ณ  ์ƒˆ ๊ฐ’์ด ์žˆ์œผ๋ฉด ๊ต์ฒดํ•œ๋‹ค. ๊ฒฝ์Ÿ์—์„œ ์ง„ ์š”์ฒญ์€ ์›๋ž˜ issuerยทresource์— ์†ํ•œ connected ์ƒํƒœ์˜ ์Šน์ž grant๋งŒ ์‚ฌ์šฉํ•œ๋‹ค.

ํšŒ์ „ํ˜• refresh credential์€ provider ์š”์ฒญ ์ „์— DB claim์„ ํ™•๋ณดํ•œ ํ”„๋กœ์„ธ์Šค๋งŒ ์‚ฌ์šฉํ•œ๋‹ค. ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค๋Š” ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ณ , ๋งŒ๋ฃŒ๋œ claim์ด๋‚˜ ์ „์†ก ๊ฒฐ๊ณผ๊ฐ€ ๋ถˆ๋ช…ํ™•ํ•œ ๊ฐฑ์‹ ์€ ๋‹ค์‹œ ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ธฐ์กด credential ๋ฐ์ดํ„ฐ๋Š” ๋ณด์กดํ•˜์ง€๋งŒ needs_reauth๋กœ ํ‘œ์‹œํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค์‹œ ์ธ์ฆํ•˜๋„๋ก ํ•œ๋‹ค. 403 insufficient_scope๋Š” ๊ทธ ์š”์ฒญ์˜ typed challenge์—์„œ scope๋ฅผ ์–ป์–ด ์žฌ์ธ๊ฐ€์— ๋ณดํƒ ๋‹ค. ๋ณ‘๋ ฌ ๋„๊ตฌ ํ˜ธ์ถœ์˜ ๊ฒฐ๊ณผ๋ฅผ ์„ธ์…˜ ์ „์ฒด์˜ ๋งˆ์ง€๋ง‰ challenge๋กœ ํ•ด์„ํ•˜์ง€ ์•Š๋Š”๋‹ค.

token์€ registryยทAgent header๋ณด๋‹ค ์šฐ์„ ํ•˜๋Š” ๋งˆ์ง€๋ง‰ credential์ด๋‹ค. OAuth ์„œ๋ฒ„๋Š” ํ˜ธ์ถœ์ž์˜ ์œ ํšจํ•œ ๊ฐœ์ธ grant๊ฐ€ ์—†์œผ๋ฉด warning๊ณผ ํ•จ๊ป˜ ์ œ์™ธํ•œ๋‹ค. Agent ์†Œ์œ ์žยท๋‹ค๋ฅธ ์‚ฌ์šฉ์žยทregistry์˜ ์ •์  ์ธ์ฆ์œผ๋กœ ๋Œ€์‹  ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š”๋‹ค. ์˜ˆ์•ฝ ์‹ ์› header๋Š” credential๋กœ ๊ณ„์‚ฐํ•˜์ง€ ์•Š๋Š”๋‹ค.

Workspace์™€ ์ฝ”๋”ฉ ์ž‘์—…

Workspace๋Š” chat ์†Œ์œ ์ž์—๊ฒŒ๋งŒ ๊ณต๊ฐœ๋˜๋ฉฐ ์‹คํ–‰ยท์Šน์ธ์€ ํ˜„์žฌ Agent ์ ‘๊ทผ๋„ ๋‹ค์‹œ ํ™•์ธํ•œ๋‹ค. Workspace ์„ค์ • ์“ฐ๊ธฐ๋Š” Agent ์†Œ์œ ์ž์—๊ฒŒ ํ•œ์ •ํ•˜๊ณ  revision ์กฐ๊ฑด๊ณผ Agent ์ˆ˜๋ช… ๊ฒฝ๊ณ„๋กœ ๋ณดํ˜ธํ•œ๋‹ค. Agent์˜ ๋„๊ตฌ ํ™œ์„ฑํ™”๋Š” ํ•ด๋‹น Agent์— ๋ช…์‹œ์ ์œผ๋กœ ์—ฐ๊ฒฐํ•œ GitHub MCP ๊ณ„์ •์„ ์ •์ฑ… ๋ฒ”์œ„ ๋‚ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ํ—ˆ์šฉํ•œ๋‹ค. Runtime ๋ชจ๋ธ ์„ ํƒ์€ ๊ด€๋ฆฌ์ž์—๊ฒŒ ํ•œ์ •ํ•˜๋ฉฐ ๋„๊ตฌ๋ฅผ ๋„๋ฉด ์ƒˆ ์‹คํ–‰ยทGit ์Šน์ธ์„ ๊ฑฐ์ ˆํ•œ๋‹ค. ๋“ฑ๋ก ์ €์žฅ์†Œยท์ •ํ™•ํ•œ ์†Œ์œ ์ž ํ—ˆ์šฉ์€ DB์—์„œ ํ˜„์žฌ ๊ฐ’์„ ์ฝ์œผ๋ฉฐ ์ผ๋ฐ˜ Agent๊ฐ€ ์ˆ˜์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์†Œ์œ ์ž ํ—ˆ์šฉ์€ ํ•ด๋‹น ๊ณ„์ •์˜ ํ–ฅํ›„ ์ €์žฅ์†Œ๋„ ํฌํ•จํ•˜๋ฏ€๋กœ ๊ด€๋ฆฌ ํ™”๋ฉด์—์„œ ๊ทธ ๋ฒ”์œ„๋ฅผ ๋ช…์‹œํ•œ๋‹ค. ์กฐํšŒ ์‹คํŒจ๋Š” ์ ‘๊ทผ์„ ๊ฑฐ์ ˆํ•˜๊ณ  selected์˜ ๋นˆ ๋ชฉ๋ก์€ ๋ชจ๋“  Git ๋Œ€์ƒ์„ ์ฐจ๋‹จํ•œ๋‹ค. all์€ ํ•ด๋‹น Agent์˜ GitHub MCP ๊ถŒํ•œ ์•ˆ์—์„œ ๋ชจ๋“  ์ €์žฅ์†Œ๋ฅผ ํ—ˆ์šฉํ•œ๋‹ค. new์˜ ์ž๋™ ๋“ฑ๋ก์€ ์„œ๋ฒ„์˜ ์‹ค์ œ ์ƒ์„ฑ ์„ฑ๊ณต์—๋งŒ ์ ์šฉํ•˜๊ณ  MCP ๊ฒฐ๊ณผ๋‚˜ ๋ชจ๋ธ์ด ์ฃผ์žฅํ•œ ์ƒ์„ฑ ์‚ฌ์‹คยท์‹œ๊ฐ์„ ๊ถŒํ•œ ๊ทผ๊ฑฐ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ƒ์„ฑ receipt์™€ ๋“ฑ๋ก์€ ์›์ž์ ์œผ๋กœ ์ €์žฅํ•˜๋ฉฐ ๋ถˆ๋ช…ํ™•ํ•œ ์ƒ์„ฑ์€ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ €์žฅ์†Œ ์ •์ฑ…์€ Sandbox ์ž์›ยท๋„คํŠธ์›Œํฌ๋‚˜ Agent์˜ GitHub MCP ์ž๊ฒฉ์ฆ๋ช…, ๊ฐœ๋ณ„ ๊ฒŒ์‹œ ์Šน์ธ์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ณ€๊ฒฝ์€ settings.update ๊ฐ์‚ฌ์— ๊ธฐ๋กํ•œ๋‹ค. Sandbox์—๋Š” ํ˜ธ์ŠคํŠธ mount, Docker socket, ๋ฐฐํฌ ์ž๊ฒฉ์ฆ๋ช…๊ณผ ์žฅ๊ธฐ Git ์ž๊ฒฉ์ฆ๋ช…์„ ์ „๋‹ฌํ•˜์ง€ ์•Š๋Š”๋‹ค. Git ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋Š” root ์†Œ์œ ๋กœ ๋‘๊ณ  Agent์˜ ์‹คํ–‰ ๊ณ„์ •์€ ์ž‘์—… ํŒŒ์ผ๋งŒ ์ˆ˜์ •ํ•œ๋‹ค. GitHub APIยทcloneยทpush๋Š” ํ˜„์žฌ Agent์˜ GitHub MCP ์ธ์ฆ์„ ์‚ฌ์šฉํ•˜๋ฉฐ Plugin ํ† ํฐ์ด๋‚˜ ๋‹ค๋ฅธ Agent์˜ ์—ฐ๊ฒฐ๋กœ ํด๋ฐฑํ•˜์ง€ ์•Š๋Š”๋‹ค. OAuth issuerยทresourceยทclient ๊ฒ€์‚ฌ์™€ ๊ฐฑ์‹ ์€ ๊ธฐ์กด MCP ์ธ์ฆ ์ œ๊ณต์ž๊ฐ€ ์ˆ˜ํ–‰ํ•œ๋‹ค. OAuth ํ† ํฐ์€ ๋ฐœ๊ธ‰ํ•œ GitHub authority์™€ ์ผ์น˜ํ•˜๋Š” APIยทGit endpoint์—๋งŒ ์‚ฌ์šฉํ•œ๋‹ค. ์„œ๋ฒ„์˜ ์ž„์‹œ bare Git ์ €์žฅ์†Œ์—์„œ ์ธ์ฆํ•˜๊ณ  Sandbox์—๋Š” ์ž๊ฒฉ์ฆ๋ช… ์—†๋Š” bundle๋งŒ ์ „๋‹ฌํ•œ๋‹ค. ์„œ๋ฒ„์—์„œ๋Š” ์ €์žฅ์†Œ checkoutยทhookยทbuild script๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ณต๊ฐœ Git endpoint๋Š” HTTPS์™€ DNS pinning์„ ์‚ฌ์šฉํ•˜๊ณ  ๋‚ด๋ถ€ ํ˜ธ์ŠคํŠธ ์˜ˆ์™ธ๋Š” ๋ฐฐํฌ์˜ ๋ณ„๋„ ๋ชฉ๋ก์„ ๋”ฐ๋ฅธ๋‹ค.

ํšจ๊ณผ๋Š” ๊ฒ€ํ† ํ•œ tree/HEAD์™€ ๊ถŒํ•œ ๊ทผ๊ฑฐ๋ฅผ ๊ฐ€์ง„ ๋™์ž‘ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋จผ์ € claimํ•œ ๋’ค ์‹คํ–‰ํ•œ๋‹ค. ์ฝ”๋”ฉ ์š”์ฒญ์˜ ์ž‘์—… ๋ธŒ๋žœ์น˜ CommitยทPushยทPR์€ ์ถ”๊ฐ€ ํ™•์ธ ์—†์ด ์‹คํ–‰ํ•˜๋ฉฐ authorization: coding-request๋กœ ๊ธฐ๋กํ•œ๋‹ค. main ๋ฐ˜์˜ยทํƒœ๊ทธยท๋ฆด๋ฆฌ์ฆˆยท๋ฐฐํฌ๋Š” ๋ณ„๋„ ์‚ฌ์šฉ์ž ํ™•์ธ์„ ์œ ์ง€ํ•œ๋‹ค. ์ž๋™ ๊ฒŒ์‹œ๋„ ์†Œ์œ ๊ถŒยทํ˜„์žฌ ์ •์ฑ…ยทfingerprintยทlease๋ฅผ ๋‹ค์‹œ ๊ฒ€์‚ฌํ•œ๋‹ค. ์ข…๋ฃŒยท์‚ญ์ œ๊ฐ€ ๋จผ์ € ๊ธฐ๋ก๋˜๋ฉด pending/์‹คํ–‰ claim์„ ๊ฑฐ์ ˆํ•œ๋‹ค. ์ข…๋ฃŒ๋œ Workspace์˜ ์ƒˆ Git ๊ฒ€ํ† ๋Š” ์†Œ์œ ์žยท์‚ด์•„ ์žˆ๋Š” ChatยทAgent์™€ action lease๋ฅผ ์›์ž์ ์œผ๋กœ ํ™•์ธํ•œ ๋’ค ๋ณต์›ํ•˜๋ฉฐ ์‚ญ์ œ๋Š” ๋˜๋Œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. main ๋ณ‘ํ•ฉ์€ PR ์†Œ์œ  ๋ฒ”์œ„์™€ CI๋ฅผ ์žฌํ™•์ธํ•˜๊ณ  GitHub์˜ ์ •ํ™•ํ•œ head SHA ์กฐ๊ฑด์„ ์‚ฌ์šฉํ•œ๋‹ค. ๋ฐฐํฌ๋Š” ํ—ˆ์šฉ๋œ main workflow์™€ ์Šน์ธํ•œ inputs๋กœ๋งŒ ์š”์ฒญํ•œ๋‹ค. ์ „์†ก ์˜ค๋ฅ˜ ์ดํ›„์˜ ๋ถˆํ™•์‹คํ•œ ํšจ๊ณผ๋Š” ์ž๋™ ์žฌ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. main ์ง์ ‘ ํ‘ธ์‹œ๋Š” ๊ฒ€ํ† ํ•œ main SHA์™€ ๊ฒŒ์‹œ๋œ ์ž‘์—… HEAD๋ฅผ ๋Œ€์กฐํ•˜๊ณ  force: false๋กœ ์‹คํ–‰ํ•œ๋‹ค. ๊ฒ€์‚ฌ ๋ฏธ๋ณด๊ณ ๋Š” none์œผ๋กœ ์Šน์ธ ํ™”๋ฉด์— ํ‘œ์‹œํ•˜๊ณ , ๋Œ€๊ธฐ ์ค‘ยท์‹คํŒจํ•œ ๊ฒ€์‚ฌ์™€ ๊ตฌ๋ถ„ํ•œ๋‹ค. ๋ชจ๋“  main ๋ฐ˜์˜์€ GitHub ๋ธŒ๋žœ์น˜ ๊ทœ์น™์„ ๋”ฐ๋ฅด๋ฉฐ ๊ถŒํ•œยท๋ณดํ˜ธ ๊ทœ์น™์„ ์šฐํšŒํ•˜๋Š” ์˜ต์…˜์„ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. ํƒœ๊ทธ๋Š” ๊ฒ€ํ† ํ•œ ์›๊ฒฉ main, ๋ฆด๋ฆฌ์ฆˆ๋Š” ๊ฒ€ํ† ํ•œ ๊ธฐ์กด ํƒœ๊ทธ์˜ ์ •ํ™•ํ•œ commit์„ ์‚ฌ์šฉํ•œ๋‹ค. ํƒœ๊ทธ๋ฅผ ๋ฎ์–ด์“ฐ์ง€ ์•Š์œผ๋ฉฐ GitHub์˜ ์ €์žฅ์†Œ ๋ฒ”์œ„ contents ์“ฐ๊ธฐ ๊ถŒํ•œ์œผ๋กœ ์‹คํ–‰ํ•˜๊ณ  ์ƒ์„ฑ ์‘๋‹ต๊ณผ ๋ฆด๋ฆฌ์ฆˆ ํƒœ๊ทธ๋ฅผ ๊ฒ€์ฆํ•œ๋‹ค. /api/workspaces/github/webhook์€ ์„œ๋ช…๊ณผ delivery ID๋กœ PR ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋งŒ ๊ฐฑ์‹ ํ•˜๋ฉฐ ์Šน์ธ ๊ถŒํ•œ์ด ์—†๋‹ค. Agent Trigger์ธ /api/webhook/{agent}๋Š” ๋ณ„๋„ Agent ์‹œํฌ๋ฆฟ์œผ๋กœ ์‹คํ–‰์„ ์‹œ์ž‘ํ•œ๋‹ค. ๊ด€๋ฆฌ์ž๊ฐ€ githubReview๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋ฉด ์„œ๋ช…๋œ PR ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด ํ•ด๋‹น Agent์˜ GitHub MCP ์—ฐ๊ฒฐ๋กœ ๋ฆฌ๋ทฐ ๋Œ“๊ธ€์„ ๊ฒŒ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค. ์ž๋™ ๋ฆฌ๋ทฐ ๊ฒŒ์‹œ ์„ค์ •์€ ๊ด€๋ฆฌ์ž๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉฐ, ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ์ €์žฅ์†Œ ์ „์ฒด ๋˜๋Š” ๋ช…์‹œ์  ์ €์žฅ์†Œ ๋ชฉ๋ก์œผ๋กœ ํ•œ์ •ํ•œ๋‹ค. PR์˜ ๋ณธ๋ฌธยทURL์ด ๊ฒŒ์‹œ ๋ชฉ์ ์ง€๋ฅผ ๊ฒฐ์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ณต๊ธ‰์ž API๊ฐ€ ํ™•์ธํ•œ base repositoryยทPR ๋ฒˆํ˜ธยทcommit_id์— COMMENT๋งŒ ๊ฒŒ์‹œํ•œ๋‹ค. ๋ชจ๋ธ์—๋Š” Skill๊ณผ ๊ฒ€์ฆ๋œ PRยทHEADยทbase์— ๊ณ ์ •๋œ ReviewSource๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ํŒŒ์ผ ๊ฒฝ๋กœ๋Š” ์ƒ๋Œ€ ๊ฒฝ๋กœ๋กœ ๊ฒ€์ฆํ•˜๊ณ  ๊ณต๊ธ‰์ž API๋งŒ ํ˜ธ์ถœํ•˜๋ฉฐ, ๋ฐ˜ํ™˜๋œ download URLยทsubmodule URL์„ ๋”ฐ๋ผ๊ฐ€์ง€ ์•Š๋Š”๋‹ค. ์ฝ๊ธฐ ์š”์ฒญ ์ „ํ›„์— ์ปค๋ฐ‹๊ณผ ํ˜„์žฌ ์—ฐ๋™ ์œ„์ž„์„ ํ™•์ธํ•œ๋‹ค. ์™„์ „ํ•œ ๋ณ€๊ฒฝ ์ž๋ฃŒ๊ฐ€ ํ™•๋ณด๋˜์ง€ ์•Š์€ ์‹คํ–‰์€ ์ „์ฒด ๋ฆฌ๋ทฐ๋กœ ๊ฒŒ์‹œํ•˜์ง€ ์•Š๋Š”๋‹ค. PR ๋ฆฌ๋ทฐ Workspace๋Š” ๊ฐœ์ธ Webhook ํ† ํฐ ๋ฐœ๊ธ‰์ž์˜ ํ˜„์žฌ ๊ถŒํ•œ๊ณผ Agent ์ €์žฅ์†Œ ์ •์ฑ…์œผ๋กœ ์ƒ์„ฑํ•œ๋‹ค. ๊ฒ€์ฆ๋œ ์ปค๋ฐ‹์˜ bare bundle๋งŒ Sandbox์— ์ „๋‹ฌํ•˜๊ณ  ์„œ๋ฒ„ Git ์ž๊ฒฉ ์ฆ๋ช…์€ ๋„ฃ์ง€ ์•Š๋Š”๋‹ค. command ๋Ÿฐํƒ€์ž„๊ณผ ์ด ๋ฆฌ๋ทฐ์˜ Workspace๋งŒ ์ œ๊ณตํ•˜๋ฉฐ ์ €์žฅ์†Œ ์ƒ์„ฑยท์—ฐ๊ฒฐ, ๋‹ค๋ฅธ Workspace ์„ ํƒ, Git publicationยท๋ฐฐํฌ๋ฅผ ๊ฑฐ์ ˆํ•œ๋‹ค. ๋๋‚˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ ๊ฒฐ๊ณผ๋ฅผ ์ฝ์ง€ ์•Š์€ ๊ฒ€์‚ฌ๋Š” ๊ฒŒ์‹œ๋ฅผ ๋ง‰๋Š”๋‹ค. ์„ฑ๊ณตยท์‹คํŒจ ๋ชจ๋‘ Workspace ์ •๋ฆฌ๋ฅผ ์š”์ฒญํ•˜๊ณ  ์‹ค์ œ ์ข…๋ฃŒ๋ฅผ ํ™•์ธํ•˜๋ฉฐ ๊ฒŒ์‹œ ์˜์ˆ˜์ฆ์€ ์ •๋ฆฌ ์‹คํŒจ์—๋„ ๋ณด์กดํ•œ๋‹ค. ๊ฐœ์ธ Webhook ํ† ํฐ์€ ๋ฐœ๊ธ‰ ์‚ฌ์šฉ์ž์˜ ๊ถŒํ•œ์œผ๋กœ ์„ ํƒ ๋ฒ”์œ„์˜ ๋ฆฌ๋ทฐ๋ฅผ ์š”์ฒญํ•œ๋‹ค. ํ† ํฐยท๊ณ„์ • ๊ถŒํ•œ ํšŒ์ˆ˜๋Š” ์ƒˆ ์กฐํšŒยท๋„๊ตฌ ํ˜ธ์ถœยท๊ฒŒ์‹œยทํ ์‹คํ–‰์— ์ ์šฉํ•œ๋‹ค. Workspace๋Š” actor ์ข…๋ฅ˜์™€ ๋ณ„๋„๋กœ ํ‘œ๋ฉด์ด ํ™•์ธํ•œ ๊ด€๋ฆฌ ์‚ฌ์šฉ์ž์˜ ํ˜„์žฌ ๊ณ„์ •ยทAgent ์ ‘๊ทผ๊ณผ ์ €์žฅ์†Œ ์ •์ฑ…์„ ๊ฒ€์‚ฌํ•œ๋‹ค. ๋Œ€ํ™”ํ˜• ์‹คํ–‰๊ณผ ์ž๋™ํ™” ๋ชจ๋‘ member ์ด์ƒ์ด์–ด์•ผ ํ•œ๋‹ค. ๊ฐœ์ธ API token์€ ๋ฐœ๊ธ‰ ์‚ฌ์šฉ์ž๋ฅผ, ๋ฉ”์‹ ์ €๋Š” ์—ฐ๊ฒฐํ•œ Studio ์‚ฌ์šฉ์ž๋ฅผ, Schedule์€ ๋“ฑ๋ก์ž๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. Schedule์˜ ๋“ฑ๋ก์ž ID๋Š” ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์œผ๋ฉฐ ํ˜„์žฌ ๊ณ„์ •ยทAgent ์ ‘๊ทผ์„ ์‹คํ–‰ ์ „์— ๋‹ค์‹œ ํ™•์ธํ•œ๋‹ค. Webhook ํ˜ธ์ถœ์ž๋Š” ๊ฐœ์ธ ํ† ํฐ ๋ฐœ๊ธ‰์ž์ด๋ฉฐ ๊ณต์œ  ์„ค์ •์—์„œ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ๊ถŒํ•œ์„ ์œ„์ž„ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์™ธ๋ถ€ payload๋Š” emailยทactor๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์—†๋‹ค. ํ”Œ๋žซํผ actor๋Š” ์›๋ž˜ ์—ฐ๋™ ํ˜ธ์ถœ์ž๋กœ ์œ ์ง€ํ•œ๋‹ค. ํ ์ž‘์—… ์‹คํ–‰ ์ง์ „์—๋„ ์‚ฌ์šฉ์ž ๊ถŒํ•œ์„ ๋‹ค์‹œ ํ™•์ธํ•˜๋ฉฐ ์™ธ๋ถ€ ํ˜ธ์ถœ์€ Git ์Šน์ธ์„ ์ง์ ‘ ์†Œ๋น„ํ•  ์ˆ˜ ์—†๋‹ค. ์Šน์ธยทCI ๊ฒฐ๊ณผ์˜ Chat ์žฌ๊ฐœ๋Š” ์›๋ž˜ ์†Œ์œ ์žยทAgent ์ ‘๊ทผยทWorkspace ์„ ํƒ๊ณผ SDK Session์„ ๋‹ค์‹œ ํ™•์ธํ•œ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ ์ด๋ฒคํŠธ๋Š” ์ƒˆ ์‚ฌ์šฉ์ž ์š”์ฒญ์ด๋‚˜ ๋‹ค์Œ Git ๋™์ž‘์— ๋Œ€ํ•œ ์Šน์ธ์œผ๋กœ ์ทจ๊ธ‰ํ•˜์ง€ ์•Š๋Š”๋‹ค.

Native CLI ๋ชจ๋ธ ์ธ์ฆ

Native CLI์—๋Š” ๊ณต๊ธ‰์ž API ํ‚ค ๋Œ€์‹  ํ•˜๋‚˜์˜ Workspace Run์— ํ•œ์ •๋œ ์„œ๋ช… ํ† ํฐ์„ ์ „๋‹ฌํ•œ๋‹ค. AES_ENCRYPTION_KEY์—์„œ ๋ณ„๋„ HKDF ๋ชฉ์  ํ‚ค๋ฅผ ํŒŒ์ƒํ•˜๋ฉฐ ์˜ค๋ธŒ์ ํŠธ URL ์„œ๋ช…๊ณผ ๊ตํ™˜ํ•  ์ˆ˜ ์—†๋‹ค. Run ๋งŒ๋ฃŒยท์ทจ์†Œยท์ข…๋ฃŒยท์›๋ž˜ ์ธ์ฆ ์ˆ˜๋‹จ ์ฒ ํšŒ๋Š” ๋‹ค์Œ ๋ชจ๋ธ ์š”์ฒญ์„ ๊ฑฐ์ ˆํ•œ๋‹ค. ์š”์ฒญ์ž์˜ Authorization, ์ฟ ํ‚ค, ์ž„์˜ ํ—ค๋”๋ฅผ ๊ณต๊ธ‰์ž์—๊ฒŒ ์ „๋‹ฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ณต์œ  Provider์— ์ €์žฅ๋œ ๋Œ€ํ™”ยทpromptยทํŒŒ์ผยท๋„๊ตฌ ๋ฆฌ์†Œ์Šค๋Š” ์ฐธ์กฐํ•  ์ˆ˜ ์—†์œผ๋ฉฐ Responses๋Š” ์ €์žฅ์„ ๋ˆ๋‹ค. Gateway ์ฃผ์†Œ๋Š” ๋ฐฐํฌ์ž๊ฐ€ ์ง€์ •ํ•˜๊ณ  Sandbox ๋„คํŠธ์›Œํฌ์—์„œ Studio์— ํ•„์š”ํ•œ ํ†ต์‹ ๋งŒ ํ—ˆ์šฉํ•œ๋‹ค. Workspace ๊ณ„์•ฝ์„ ๋”ฐ๋ฅธ๋‹ค.

SDK Session๊ณผ ์Šน์ธ ์ƒํƒœ

SDK Session ์ด๋ ฅ๊ณผ ์Šน์ธ ๋Œ€๊ธฐ RunState๋Š” runtime_sessions์— ๋ณ„๋„๋กœ ์ €์žฅํ•œ๋‹ค. ๊ฐ™์€ ๋ฐฐํฌ์˜ AES_ENCRYPTION_KEY๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ ๋Œ€ํ™” ID์™€ ์†Œ์œ ์ž๋ฅผ ์ธ์ฆ ๋ฐ์ดํ„ฐ์— ๋ฌถ์€ ์•”ํ˜ธ๋ฌธ๋งŒ ์ฝ๋Š”๋‹ค. ์›๋ฌธ ์ƒํƒœ์—๋Š” ๋ชจ๋ธ/๋„๊ตฌ ์ด๋ ฅ๊ณผ ๊ฐ Agent์˜ PII ๋ณต์› ๋งคํ•‘์ด ํฌํ•จ๋  ์ˆ˜ ์žˆ๋‹ค. DB ์ ‘๊ทผ ๊ถŒํ•œ๊ณผ ์•”ํ˜ธํ™” ํ‚ค๋ฅผ ๋ถ„๋ฆฌํ•ด ๊ด€๋ฆฌํ•˜๊ณ  ๋ฐฑ์—…ยท๋ณต๊ตฌ ์‹œ ๊ฐ™์€ ํ‚ค๋ฅผ ์œ ์ง€ํ•˜๋ผ.

์•”ํ˜ธํ™”๋œ Session์—๋Š” ์ตœ์ดˆ ์‹คํ–‰์˜ Studio ์‚ฌ์šฉ์ž ID๋ฅผ ์ €์žฅํ•œ๋‹ค. ์ƒˆ ํ„ด๊ณผ ์Šน์ธ ์žฌ๊ฐœ๋Š” ๋กœ๊ทธ์ธ ์„ธ์…˜์˜ ID์™€ ์ผ์น˜ํ•ด์•ผ ํ•˜๋ฉฐ ์ด๋ฉ”์ผ๋งŒ์œผ๋กœ ๋‹ค๋ฅธ ๊ณ„์ •์— ์ด๋ ฅ์„ ์ธ๊ณ„ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์‚ฌ์šฉ์ž ID๊ฐ€ ์—†๊ฑฐ๋‚˜ ๋‹ค๋ฅธ Session์€ ์‹คํ–‰ํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ƒˆ Chat์„ ์‹œ์ž‘ํ•ด์•ผ ํ•œ๋‹ค. Workspace Git ์Šน์ธ์€ ์š”์ฒญ์ž์˜ Studio ์‚ฌ์šฉ์ž ID๋ฅผ ๋ณด๊ด€ํ•˜๊ณ  ๊ฐ™์€ ๊ณ„์ •์˜ ๊ฒฐ์ •๋งŒ ํ—ˆ์šฉํ•œ๋‹ค. ํ›„์† ์‹คํ–‰ ํ๋Š” ์ด ID๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์œผ๋ฉฐ ํ˜„์žฌ ๊ณ„์ •ยทAgent ์ ‘๊ทผ๊ณผ SDK Session์˜ ์‚ฌ์šฉ์ž ID๋ฅผ ๋‹ค์‹œ ๊ฒ€์‚ฌํ•œ๋‹ค.

์Šน์ธ์€ Chat ์†Œ์œ ์ž๊ฐ€ ์ •ํ™•ํ•œ revision๊ณผ ํ•ญ๋ชฉ ID๋ฅผ ์ง€์ •ํ•œ๋‹ค. ๋™์ผ ์ถœ์ฒ˜ session ๋ณ€๊ฒฝ ๊ฒ€์‚ฌ, Agent ์ ‘๊ทผ ์žฌํ™•์ธ, ์‹คํ–‰ lease์™€ Session CAS๋ฅผ ํ•จ๊ป˜ ์ ์šฉํ•œ๋‹ค. ์Šน์ธ ์ƒํƒœ๋Š” ๋„๊ตฌ ์‹คํ–‰ ์ „์— pending์—์„œ running์œผ๋กœ ์„ ์ ํ•œ๋‹ค. ์ค‘๋ณตยท๋‚ก์€ ๊ฒฐ์ •, ๋ณ€๊ฒฝ๋œ ์„ค์ •๊ณผ ์—ฐ๊ฒฐ์€ ๊ฑฐ๋ถ€ํ•˜๋ฉฐ ์Šน์ธ ํ›„ ์ค‘๋‹จ๋œ ์‹คํ–‰์€ ์ž๋™์œผ๋กœ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. ์‚ญ์ œ tombstone์€ ๋Šฆ๊ฒŒ ๋๋‚œ ์‹คํ–‰์˜ ์žฌ์ƒ์„ฑ์„ ๋ง‰๋Š”๋‹ค.

SDK tracing์€ ๋กœ์ปฌ processor๊ฐ€ ์ด๋ฆ„ยท์‹œ๊ฐ„ยท์ƒํƒœยท์‚ฌ์šฉ๋Ÿ‰๋งŒ ์ˆ˜์ง‘ํ•œ๋‹ค. SDK์˜ ๊ณต๊ฐœ exporter๋ฅผ ์„ค์น˜ํ•˜์ง€ ์•Š์œผ๋ฉฐ ์›๋ฌธ ๋ชจ๋ธ/๋„๊ตฌ ์ž…๋ ฅยท์ถœ๋ ฅ๊ณผ credential์„ native span์œผ๋กœ ๋‚ด๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค. ๋ฐฐํฌ๊ฐ€ ์„ ํƒํ•œ OTLP exporter์™€ ์‹คํ–‰ ์˜ค๋ฅ˜/๊ฒฝ๊ณ  ๊ธฐ๋ก์˜ ์ ‘๊ทผ ๋ฒ”์œ„๋Š” ๊ธฐ์กด ์šด์˜ ์ •์ฑ…์„ ๋”ฐ๋ฅธ๋‹ค.

PII ํ•„ํ„ฐ๋ง, ๊ทธ๋ฆฌ๊ณ  ๊ทธ๊ฒƒ์ด ๋ฉˆ์ถ”๋Š” ๊ณณ

parameters.piiFiltering์€ Agent๋ณ„ ์„ ํƒ ๊ธฐ๋Šฅ์ด๋‹ค. SDK ๋ชจ๋ธ ์š”์ฒญ์˜ ํ…์ŠคํŠธ์™€ system prompt์—์„œ ํƒ์ง€ํ•œ ๊ฐ’์„ [[PII:โ€ฆ]]๋กœ ์น˜ํ™˜ํ•˜๊ณ  ํ‘œ์‹œํ•  ์‘๋‹ต์—์„œ ๋ณต์›ํ•œ๋‹ค. ์ŠคํŠธ๋ฆฌ๋ฐ์˜ ํ† ํฐ ๊ฒฝ๊ณ„์™€ ํ•˜์œ„ Agent์˜ ์น˜ํ™˜ ๋งคํ•‘๋„ ์œ ์ง€ํ•œ๋‹ค. ์†Œ์œ  ์ฝ”๋“œ๋Š” application/llm/pii.ts, runtime/model.ts, runtime/tools.ts๋‹ค.

๊ฒฝ๊ณ„ ์ „๋‹ฌยท๋ณด๊ด€ํ•˜๋Š” ๋‚ด์šฉ
ํ…์ŠคํŠธ ๋ชจ๋ธ ์š”์ฒญยท๋ชจ๋ธ์— ๋Œ์•„๊ฐ€๋Š” ๋„๊ตฌ ๊ฒฐ๊ณผ ํƒ์ง€ํ•œ PII๋ฅผ ์น˜ํ™˜ํ•œ๋‹ค
์ƒˆ ChatยทWorkspace์˜ Agent ์ถ”์ฒœ Agent ์„ ํƒ ์ „์—๋„ ์š”์ฒญ ํ…์ŠคํŠธ์™€ ํ›„๋ณด ์„ค๋ช…์˜ ํƒ์ง€ํ•œ PII๋ฅผ ์น˜ํ™˜ํ•ด ๊ฒฐ์ • ๋ชจ๋ธ์— ๋ณด๋‚ธ๋‹ค. ํ˜ธ์ถœ์€ ์‚ฌ์šฉ์ž๋ณ„ ๋ถ„ยท์ผ ์ƒํ•œ์„ ๋ฐ›๋Š”๋‹ค
MCPยทFetchUrlยทํŒŒ์ผ ์ƒ์„ฑ/ํŽธ์ง‘ยทWorkspace ๋“ฑ ์‹ค์ œ ๋„๊ตฌ dispatch ๋ณต์›ํ•œ ์ธ์ž๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์ˆ˜์‹  ์‹œ์Šคํ…œ์€ ๊ทธ ๊ฐ’์„ ๋ณธ๋‹ค
Agent์˜ GenerateImageยทEditImage prompt ์น˜ํ™˜๋œ ์ธ์ž๋ฅผ ์ด๋ฏธ์ง€ ๋ชจ๋ธ์— ์ „๋‹ฌํ•œ๋‹ค. ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์ด๋Š” prompt๋Š” ๋ณต์›ํ•œ๋‹ค
ํ•˜์œ„ Agent ์š”์ฒญ ์น˜ํ™˜๋œ ๋ฉ”์‹œ์ง€์™€ ํ•„์š”ํ•œ ๋งคํ•‘์„ ์ „๋‹ฌํ•œ๋‹ค
์‚ฌ์šฉ์ž ์‘๋‹ตยทChat ํ™”๋ฉด ๊ธฐ๋กยท์ƒ์„ฑ ํŒŒ์ผ ๋ณต์›ํ•œ ๋‚ด์šฉ์ด๋‹ค. ์ €์žฅ ์ต๋ช…ํ™” ๊ธฐ๋Šฅ์ด ์•„๋‹ˆ๋‹ค
capability embeddingยทrerank ์ตœ๊ทผ ์š”์ฒญ๊ณผ ์ œํ•œ๋œ recall ๋ฌธ๋งฅ์„ ์ค€๋น„ ๋‹จ๊ณ„์—์„œ ์›๋ฌธ์œผ๋กœ ๋ณด๋‚ธ๋‹ค
์ž๋™ Memory recall ์ตœ์‹  ์š”์ฒญ์„ ์—ฐ๊ฒฐ๋œ MCP์— ์›๋ฌธ์œผ๋กœ ๋ณด๋‚ธ๋‹ค. ํšŒ์ƒ ๊ฒฐ๊ณผ๊ฐ€ ๋ชจ๋ธ prompt์— ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” ํ•„ํ„ฐ๋ฅผ ์ง€๋‚œ๋‹ค
MCP ์‹ ์› ํ—ค๋” ํ™•์ธํ•œ ์ด๋ฉ”์ผ ๋“ฑ ํ”Œ๋žซํผ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํ‰๋ฌธ์œผ๋กœ ์ „๋‹ฌํ•œ๋‹ค. ํ•„ํ„ฐ ๋Œ€์ƒ์ด ์•„๋‹ˆ๋‹ค

์ด๋ฏธ์ง€ bytes์˜ ๊ฐœ์ธ์ •๋ณด ์ œ๊ฑฐ๋Š” ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ผ๋ฐ˜์ ์ธ ์ด๋ฆ„ยท์ด๋ฏธ์ง€ ์† ๊ฐœ์ธ์ •๋ณดยทํŒจํ„ด์— ๋งž์ง€ ์•Š๋Š” ๊ฐ’๋„ ํƒ์ง€ ๋Œ€์ƒ์ด ์•„๋‹ˆ๋‹ค. ํƒ์ง€ ๋ฒ”์œ„๋Š” email, ์ „ํ™”๋ฒˆํ˜ธ, ํ•œ๊ตญ ์ฃผ๋ฏผ/์™ธ๊ตญ์ธ๋“ฑ๋ก๋ฒˆํ˜ธ์˜ ์ง€์› ํ˜•์‹, Luhn ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•œ 13โ€“19์ž๋ฆฌ ๊ฒฐ์ œ ์นด๋“œ ๋ฒˆํ˜ธ๋‹ค. ๋ฒ”์šฉ DLP๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

preview๋Š” ๋ชจ๋ธ์„ ํ˜ธ์ถœํ•˜์ง€ ์•Š์•„๋„ recallยทdiscovery๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์กฐ๋ฆฝํ•œ ์›๋ฌธ์„ ๋ณด์—ฌ์ค€๋‹ค. ์ค€๋น„ ๋‹จ๊ณ„์˜ ์›๋ฌธ ์ „์†ก์ด ํ—ˆ์šฉ๋˜์ง€ ์•Š๋Š” ํ™˜๊ฒฝ์—์„œ๋Š” ํ•ด๋‹น capability์™€ ์—ฐ๊ฒฐ์„ ์„ ํƒํ•˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค. ์™ธ๋ถ€ ์ œ๊ณต์ž๊ฐ€ ๋™์ผํ•œ ๊ณ„์ •์ธ์ง€์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๊ฐ ์ฑ„๋„์˜ ์ „์†กยท๋ณด์กด ์ •์ฑ…์„ ํ™•์ธํ•œ๋‹ค.

ํšŒ์ƒ๋œ ๊ธฐ์–ต๊ณผ ์ฒจ๋ถ€ยท๋„๊ตฌ ๊ฒฐ๊ณผ๋Š” ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋Š” ํ…์ŠคํŠธ๋‹ค. rememberedBlock์€ ๊ธฐ์–ต์„ <recalled> ๊ฒฝ๊ณ„์™€ ์ธ์šฉ๋ฌธ์œผ๋กœ ๊ฐ์‹ธ ์ง€์‹œ์™€ ๊ตฌ๋ถ„ํ•˜์ง€๋งŒ prompt injection์„ ์ œ๊ฑฐํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ฌด์—‡์„ ๊ธฐ์–ตํ•˜๊ณ  ๋ˆ„๊ฐ€ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋Š” ์—ฐ๊ฒฐ๋œ Memory ์„œ๋ฒ„์˜ ์ €์žฅยท๊ถŒํ•œ ์ •์ฑ…์ด ์†Œ์œ ํ•œ๋‹ค.

ํ˜ธ์ถœ์ž ์ปจํ…์ŠคํŠธ

parameters.callerContext ๋กœ Agent๋ณ„ ์˜ตํŠธ์ธ. ์ผœ์ ธ ์žˆ์œผ๋ฉด Slack ๋Ÿฐ์€ ๋ˆ„๊ฐ€ ๋ฌป๊ณ  ์žˆ๋Š”์ง€๋ฅผ ๋ชจ๋ธ์—๊ฒŒ ์•Œ๋ ค ์ฃผ๊ณ . ํ‘œ์‹œ ์ด๋ฆ„, ์‹œ๊ฐ„๋Œ€, ์•„๋ฐ”ํƒ€์˜ URL. ํ•œ ์Šค๋ ˆ๋“œ์— ์‚ฌ๋žŒ์ด ๋‘˜ ์ด์ƒ์ด๋ฉด ํ™”์ž๋งˆ๋‹ค ๋ผ๋ฒจ์„ ๋ถ™์ธ๋‹ค. Telegram ๋Ÿฐ๊ณผ Teams ๋Ÿฐ์€ ๊ฐ์ž์˜ ์ด๋ฒคํŠธ๊ฐ€ ์‹ค์–ด ์˜ค๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ™์€ ์ผ์„ ํ•œ๋‹ค. ๋ณด๋‚ธ ์‚ฌ๋žŒ์˜ ์ด๋ฆ„, ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋ฐ–์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ์—†๋‹ค. ๋‘˜ ๋‹ค ์‹œ๊ฐ„๋Œ€๋„ email ๋„ ๋„˜๊ฒจ์ฃผ์ง€ ์•Š๋Š”๋‹ค.

์ด๋ฆ„์€ piiFiltering ์ด ๋งˆ์Šคํ‚นํ•˜์ง€ ์•Š๋Š” PII ๋‹ค. ๊ทธ ํŒจํ„ด๋“ค์€ email, ์ „ํ™”๋ฒˆํ˜ธ, ๋“ฑ๋ก๋ฒˆํ˜ธ/์นด๋“œ ๋ฒˆํ˜ธ์— ๋งž๊ณ  ์‚ฌ๋žŒ์˜ ์ด๋ฆ„์€ ๊ทธ ์–ด๋А ๊ฒƒ์—๋„ ๋งž์ง€ ์•Š์œผ๋ฏ€๋กœ, ํ˜ธ์ถœ์ž ๋ธ”๋ก์ด ์‹ฃ๋Š” ๊ฒƒ์€ ํ•„ํ„ฐ๋ง์ด ์ผœ์ ธ ์žˆ์–ด๋„ ์“ฐ์ธ ๊ทธ๋Œ€๋กœ ๋ชจ๋ธ์— ๋„๋‹ฌํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ๊ทธ ๋ธ”๋ก์€ email ์„ ์‹ฃ์ง€ ์•Š์œผ๋ฉฐ, ๊ทธ๋ž˜์„œ ์ด๊ฒƒ์ด ๊ธฐ๋ณธ ๋™์ž‘์ด ์•„๋‹ˆ๋ผ Agent๋ณ„ ์˜ตํŠธ์ธ์ด๋‹ค. ์ผœ๋Š” ๊ฒƒ์€ ์‹ค์ œ ์‚ฌ๋žŒ์˜ ์ด๋ฆ„์„ ํ”„๋กฌํ”„ํŠธ์—, ๊ทธ๋ฆฌ๊ณ  ์ œ๊ณต์ž๊ฐ€ ๋กœ๊น…ํ•˜๋Š” ๋ฌด์—‡์—๋“  ์ง‘์–ด๋„ฃ๊ฒ ๋‹ค๋Š” ๊ฒฐ์ •์ด๋‹ค.

์˜ตํŠธ์ธ์€ ๋ชจ๋ธ์— ๋„ฃ์„ ์ด๋ฆ„ยท์‹œ๊ฐ„๋Œ€ยท์•„๋ฐ”ํƒ€๋ฅผ ์œ„ํ•œ ํ”„๋กœํ•„ ์กฐํšŒ๋ฅผ ๊ฒŒ์ดํŠธํ•œ๋‹ค. Slack ์˜ private agent ์ ‘๊ทผ ๊ฒ€์‚ฌ์™€ artifact ์†Œ์œ ์ž ์‹๋ณ„์— ํ•„์š”ํ•œ email ์กฐํšŒ๋Š” ์˜ตํŠธ์ธ๊ณผ ๋…๋ฆฝ์ ์œผ๋กœ users.info ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ email ์€ ๋ชจ๋ธ์˜ caller ๋ธ”๋ก์— ๋“ค์–ด๊ฐ€์ง€ ์•Š๋Š”๋‹ค. ์ด๋ฆ„์ด ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ๋„์ฐฉํ•˜๋Š” Telegram ์—์„œ๋Š” ์˜ตํŠธ์ธํ•˜์ง€ ์•Š์€ ์ด๋ฆ„์ด ๋Œ€ํ™” ํŠธ๋žœ์Šคํฌ๋ฆฝํŠธ์— ์“ฐ์ด์ง€๋„ ์•Š๋Š”๋‹ค(๋ฐ์ดํ„ฐ ๋…ธ์ถœ๊ณผ ๋ณด์กด ์ฐธ๊ณ ). transfer ๋Š” ํ˜ธ์ถœ์ž๋ฅผ ์ž์‹์—๊ฒŒ ์‹ค์–ด ๋‚˜๋ฅด๊ณ (RunOrigin), ๊ฑฐ๊ธฐ์„œ ์ž์‹ Agent ์ž์‹ ์˜ ์˜ตํŠธ์ธ์ด ๋‹ค์‹œ ๊ฒฐ์ •ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋ฆ„์€ ๋ช‡ ํ™‰ ๋–จ์–ด์ ธ ์žˆ๋“  ๊ทธ๊ฒƒ์„ ์š”์ฒญํ•œ Agent์—๋งŒ ๋„๋‹ฌํ•˜๊ณ , ์†Œ์œ ์ž๊ฐ€ ํ•œ ๋ฒˆ๋„ ์˜ตํŠธ์ธํ•˜์ง€ ์•Š์€ agent ๋Š” ๊ทธ๊ฒƒ์„ ๊ฒฐ์ฝ” ๋ณด์ง€ ์•Š๋Š”๋‹ค. ํ•ด์„๋œ ํ”„๋กœํ•„์€ ์›Œํฌ์ŠคํŽ˜์ด์Šค๋ณ„๋กœ ๋ฉ”๋ชจ๋ฆฌ์— ์บ์‹œ๋˜๊ณ (1์‹œ๊ฐ„, ์‹คํŒจ๋Š” 1๋ถ„), ํฌ๊ธฐ๊ฐ€ ์ œํ•œ๋˜๋ฉฐ, ๊ฒฐ์ฝ” ์˜์†๋˜์ง€ ์•Š๋Š”๋‹ค.

ํ‘œ์‹œ ์ด๋ฆ„์€ ๊ณต๊ฒฉ์ž๊ฐ€ ํ†ต์ œํ•œ๋‹ค. ๋ˆ„๊ตฌ๋‚˜ ์ž๊ธฐ ๊ฒƒ์„ ๋ฌด์—‡์œผ๋กœ๋“  ์„ค์ •ํ•  ์ˆ˜ ์žˆ๊ณ , ๊ทธ๊ฒƒ์ด ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ์— ์‹ค๋ฆฐ๋‹ค. ๊ณต์œ  ์Šค๋ ˆ๋“œ์˜ ํ™”์ž ๋ผ๋ฒจ์„ ํ†ตํ•ด, ์ž๊ธฐ ๋Œ€ํ™”๋งŒ์ด ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ๋Œ€ํ™” ์•ˆ์—์„œ. callerFrom(src/domain/execution/actor.ts)์€ RunCaller ๊ฐ€ ๋งŒ๋“ค์–ด์ง€๋Š” ์œ ์ผํ•œ ์ž๋ฆฌ์ด๊ณ  ๋”ฐ๋ผ์„œ ๊ทธ ์ด๋ฆ„์„ ์•ˆ์ „ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ์œ ์ผํ•œ ์ž๋ฆฌ๋‹ค. ์ œ์–ด ๋ฌธ์ž๋Š” ์ œ๊ฑฐํ•˜๊ณ , ๊ณต๋ฐฑ์€ ํ•œ ์ค„๋กœ ์ ‘๊ณ , ์ด๋ฆ„์€ 60์ž๋กœ ์ œํ•œํ•˜๋ฉฐ, ์•„๋ฐ”ํƒ€๋Š” https: URL ์ผ ๋•Œ๋งŒ ๋ฐ›์•„๋“ค์ธ๋‹ค. ๊ทธ๊ฒƒ์ด ํ”„๋กฌํ”„ํŠธ ์ธ์ ์…˜์„ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค์ง€๋Š” ์•Š์ง€๋งŒ. ๋ฉ”์‹œ์ง€ ๋ณธ๋ฌธ๋„ ์‹ ๋ขฐ๋˜์ง€ ์•Š๋Š”๋‹ค. ์‹ ์› ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๊ฐ€ ๋…์ž๊ฐ€ ๋ณผ ์ˆ˜ ์—†๋Š” ์ง€์‹œ๋ฅผ ์ˆจ๊ธธ ์ž๋ฆฌ๊ฐ€ ๋˜๋Š” ๊ฒƒ์€ ๋ง‰๋Š”๋‹ค.

Slack ์ถœ๋ ฅ ์•Œ๋ฆผ

LLM ๋‹ต๋ณ€๊ณผ tool ๊ฒฐ๊ณผ๋Š” Slack mrkdwn์œผ๋กœ ์ „๋‹ฌ๋˜๋ฏ€๋กœ ํ…์ŠคํŠธ ์•ˆ์˜ mention token์€ ๋‹จ์ˆœํ•œ ํ‘œ์‹œ๊ฐ€ ์•„๋‹ˆ๋‹ค. <@Uโ€ฆ>๋Š” ์‚ฌ์šฉ์ž๋ฅผ, <!subteam^โ€ฆ>์€ ์‚ฌ์šฉ์ž ๊ทธ๋ฃน์„, <!channel>ยท<!here>ยท <!everyone>์€ ๋„“์€ ์ฒญ์ค‘์„ ์‹ค์ œ๋กœ ์•Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค. ๊ทธ ํ…์ŠคํŠธ๋Š” ์งˆ๋ฌธยท์™ธ๋ถ€ ๋ฌธ์„œยท๋„๊ตฌ ๊ฒฐ๊ณผ์— ์˜ํ•ด ์˜ํ–ฅ๋ฐ›์œผ๋ฏ€๋กœ ์•Œ๋ฆผ ๊ถŒํ•œ์œผ๋กœ ์ทจ๊ธ‰ํ•˜์ง€ ์•Š๋Š”๋‹ค.

neutralizeSlackMentions(src/domain/slack/outboundText.ts)๊ฐ€ ์•Œ๋ฆผ ๊ฐ€๋Šฅํ•œ ์™„์ „ํ•œ token๋งŒ escapeํ•œ๋‹ค. slackClient๋Š” chat.postMessage, chat.update, ๊ทธ๋ฆฌ๊ณ  stream์˜ text/chunk ์ถ•์„ Slack์— ์“ฐ๊ธฐ ์ง์ „์— ๋ชจ๋‘ ์ด ํ•จ์ˆ˜๋ฅผ ํ†ต๊ณผ์‹œํ‚จ๋‹ค. ๋”ฐ๋ผ์„œ streaming ์‹คํŒจ ๋’ค edit๋กœ ๋ฌผ๋Ÿฌ๋‚˜๊ฑฐ๋‚˜ ๋งˆ๊ฐ ์‹œ ๋‚จ์€ ๋‹ต์„ ๋‹ค์‹œ ๋ณด๋‚ด๋Š” ๊ฒฝ๋กœ๋„ ๊ฐ™์€ ๊ทœ์น™์„ ๋ฐ›๋Š”๋‹ค. ์ผ๋ฐ˜ Markdown, URL link, channel reference, ์•Œ๋ฆผ์„ ๋งŒ๋“ค์ง€ ์•Š๋Š” date token์€ ๋ณด์กดํ•œ๋‹ค.

Slack ์›Œํฌ์ŠคํŽ˜์ด์Šค ์ฝ๊ธฐ

parameters.slackWorkspace ๋กœ Agent๋ณ„ ์˜ตํŠธ์ธ. ์ผœ์ ธ ์žˆ์œผ๋ฉด ๋Ÿฐ์€ ์ž๊ธฐ agent ์˜ ๋ด‡์ด ์„ค์น˜๋œ ์›Œํฌ์ŠคํŽ˜์ด์Šค์˜ ์ฑ„๋„ ํžˆ์Šคํ† ๋ฆฌ, ์Šค๋ ˆ๋“œ, ์‚ฌ์šฉ์ž ์ด๋ฆ„์„ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค.

Agent ๋Š” ๊ณต์œ  ์นดํƒˆ๋กœ๊ทธ๋‹ค (private agent ๋ผ๋ฉด ๊ทธ ์ ‘๊ทผ ๋ฒ”์œ„ ์•ˆ์—์„œ). ๋”ฐ๋ผ์„œ agent ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์‚ฌ๋žŒ์€ ๋ˆ„๊ตฌ๋‚˜ ๊ทธ ๋ด‡์ด ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค. ๋ด‡์ด ์ดˆ๋Œ€๋œ ๋ชจ๋“  ์ฑ„๋„์ด๋ฉฐ, groups:history ๊ฐ€ ์ ์šฉ๋˜๋Š” ๋น„๊ณต๊ฐœ ์ฑ„๋„๋„ ํฌํ•จ์ด๋‹ค. ๊ทธ๊ฒƒ์ด ์ด๊ฒƒ์„ Slack ์— ์—ฐ๊ฒฐ๋œ ๋ชจ๋“  agent ๊ฐ€ ๊ฐ–๋Š” capability ๊ฐ€ ์•„๋‹ˆ๋ผ Agent๋ณ„ ์˜ตํŠธ์ธ์œผ๋กœ ๋งŒ๋“  ์ด์œ  ์ „๋ถ€๋‹ค. ์ผœ๋Š” ๊ฒƒ์€ ์–ด๋–ค ์ฑ„๋„์˜ ๋‚ด์šฉ์„ ๊ทธ ์ฑ„๋„ ์ž์‹ ์˜ ๋ฉค๋ฒ„๋ณด๋‹ค ๋” ๋„“์€ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ๋‹ฟ๊ฒŒ ํ•˜๊ฒ ๋‹ค๋Š” ๊ฒฐ์ •์ด๋‹ค.

๋‘ ๊ฐ€์ง€๋Š” ๋น ๋œจ๋ ค์„œ๊ฐ€ ์•„๋‹ˆ๋ผ ๊ตฌ์กฐ์ ์œผ๋กœ ๊ฑฐ๋ถ€๋œ๋‹ค:

  • ์“ฐ๊ธฐ ์—†์Œ. chat:write ๋Š” ๋ด‡์—๊ฒŒ ๋ถ€์—ฌ๋ผ ์žˆ๊ณ . ๋‹ต์žฅ ์ „์†ก์— ํ•„์š”ํ•˜๋‹ค. ์˜๋„์ ์œผ๋กœ ์–ด๋–ค ๋„๊ตฌ์—์„œ๋„ ๋‹ฟ์„ ์ˆ˜ ์—†๋‹ค. ๋Ÿฐ์€ ์ž๊ธฐ๊ฐ€ ์“ฐ์ง€ ์•Š์€ ํ…์ŠคํŠธ์— ์กฐ์ข…๋œ๋‹ค. ๊ธ€๋„ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋Š” ๋Ÿฐ์€, ์–ด๋–ค ์ฑ„๋„์— ์‹ฌ์–ด ๋‘” ๋ฉ”์‹œ์ง€๊ฐ€ ๋ด‡์œผ๋กœ ํ•˜์—ฌ๊ธˆ ๋‹ค๋ฅธ ๊ณณ์—์„œ ๋งํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ๋Ÿฐ์ด๋‹ค.

  • email ์€ ๋ชจ๋ธ์— ๋„๋‹ฌํ•˜์ง€ ์•Š๋Š”๋‹ค. users:read.email ์ด ๋ถ€์—ฌ๋ผ ์žˆ๋Š”๋ฐ๋„ ๊ทธ๋ ‡๋‹ค. SlackUser ์™€ SlackUsers ๋Š” ์ด๋ฆ„, ์งํ•จ, ์‹œ๊ฐ„๋Œ€, ์ƒํƒœ ๋ฌธ๊ตฌ, ์•„๋ฐ”ํƒ€๋กœ ๋‹ตํ•œ๋‹ค. ๋™๋ฃŒ๊ฐ€ ํ”„๋กœํ•„์„ ํด๋ฆญํ•ด์„œ ๋ณด๋Š” ์ „๋ถ€๋‹ค. ์ฃผ์†Œ๋Š” ๊ฒฐ์ฝ” ๋‹ตํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๊ฒƒ์€ ํ˜ธ์ถœ์ž ์ปจํ…์ŠคํŠธ๊ฐ€ ์ด๋ฏธ ์ ์šฉํ•˜๋Š” ๊ทœ์น™์ด๊ณ , ์ด์œ ๋„ ๊ฐ™๋‹ค. email ์€ Slack ๋ฐ–์—์„œ ์‚ฌ๋žŒ์„ ์‹๋ณ„ํ•˜๋ฉฐ, ์–ด๋–ค ๋‹ต๋„ ์ž˜ ์“ฐ์ด๊ธฐ ์œ„ํ•ด ๊ทธ๊ฒƒ์„ ํ•„์š”๋กœ ํ•˜์ง€ ์•Š๋Š”๋‹ค.

    ์ด๋ฉ”์ผ์€ private Agent ์ ‘๊ทผ ํŒ์ •, MCP ์œ„์ž„ ์‹ ์›๊ณผ Artifact์˜ ๊ฐœ์ธ ๊ท€์†์— ๋ณ„๋„๋กœ ์‚ฌ์šฉํ•œ๋‹ค. Slack actor๋Š” ๋ฐœ์‹ ์ž์˜ Slack ์‚ฌ์šฉ์ž ID๋กœ ์œ ์ง€ํ•œ๋‹ค. ๊ฐœ์ธ ์˜ˆ์‚ฐ๊ณผ ๋™์‹œ์„ฑ์€ ์ผํšŒ์šฉ ์ฝ”๋“œ๋กœ ์—ฐ๊ฒฐํ•œ Studio ์‚ฌ์šฉ์ž ID์— ํ•ฉ์‚ฐํ•œ๋‹ค. toUserDetail์€ ์ด๋ฉ”์ผ์„ ๋ชจ๋ธ์šฉ ๋„๊ตฌ ๊ฒฐ๊ณผ์— ๋ณต์‚ฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ด ์กฐํšŒ๋Š” ๋ชจ๋ธ ํ‘œ์‹œ ๋ฌธ๋งฅ์„ ์ œ์–ดํ•˜๋Š” callerContext์™€ ๋…๋ฆฝ์ ์ด๋‹ค.

์•ˆ์œผ๋กœ ์‹ค๋ ค ์˜ค๋Š” ๊ฒƒ์€ ์ฒจ๋ถ€๋œ ๋ฌธ์„œ์™€ ๋˜‘๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์‹ ๋ขฐ๋˜์ง€ ์•Š๋Š”๋‹ค. ์ฑ„๋„์˜ ๋ฉ”์‹œ์ง€๋Š” ๊ทธ ์ฑ„๋„์— ์žˆ๋Š” ๋ˆ„๊ตฌ๋“  ์“ด ๊ฒƒ์ด๊ณ , ๊ทธ๊ฒƒ์ด ํ…์ŠคํŠธ๋กœ ๋ชจ๋ธ์— ๋„๋‹ฌํ•œ๋‹ค. PII ํ•„ํ„ฐ๋ง์€ ๋‹ค๋ฅธ ๊ฒƒ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ทธ ๋„๊ตฌ ๊ฒฐ๊ณผ์—๋„ ์ ์šฉ๋˜๋ฉฐ(parameters.piiFiltering), ํ„ด๋ณ„ ๋„๊ตฌ ๊ฒฐ๊ณผ ์˜ˆ์‚ฐ์ด ๊ทธ ํฌ๊ธฐ๋ฅผ ์ œํ•œํ•œ๋‹ค. ํŠธ๋žœ์Šคํฌ๋ฆฝํŠธ์˜ ๊ธธ์ด๋Š” Slack ์ด ์ •ํ•˜๋Š” ๊ฒƒ์ด๋ฏ€๋กœ, ํ†ต์งธ๋กœ ์ฒญ๊ตฌ๋˜๋Š” ๋Œ€์‹  fit ์„ ๊ฑฐ์นœ๋‹ค.

๋ฆฌ๋”๋Š” ๋ด‡ token ์„ ์ฅ๊ณ  ์žˆ๊ณ , ๊ทธ๊ฒƒ์€ ์ƒ์„ฑ ์‹œ์ ์— ๋ฐ”์ธ๋”ฉ๋œ๋‹ค. ์›Œํฌ์ŠคํŽ˜์ด์Šค๋ฅผ ์ง€๋ชฉํ•˜๋Š” ๋„๊ตฌ ์ธ์ž๋Š” ๋ชจ๋ธ์ด ์›Œํฌ์ŠคํŽ˜์ด์Šค๋ฅผ ๊ณ ๋ฅด๊ฒŒ ๋งŒ๋“ค ๊ฒƒ์ด๊ณ , ๊ทธ๊ฒƒ์ด ์˜ฌ๋ฐ”๋ฅธ ๋ชจ์–‘์ธ ์š”์ฒญ์€ ์—†๋‹ค.

์ฒจ๋ถ€ ๋ฌธ์„œ

๋ฌธ์„œ์˜ ํ…์ŠคํŠธ๋Š” ํ„ด ์•ˆ์œผ๋กœ ๋“ค์–ด๊ฐ€๋ฏ€๋กœ, ๋ˆ„๊ตฌ๋“  ์ฒจ๋ถ€ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ๋ฌด์—‡์ด๋“  ๋งํ•  ์ˆ˜ ์žˆ๋‹ค. Slack ์ฑ„๋„์—์„œ ๊ทธ๊ฒƒ์€ ๋ฌป๋Š” ์‚ฌ๋žŒ๋งŒ์ด ์•„๋‹ˆ๋‹ค. ๋ด‡์ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ณณ์— ํŒŒ์ผ์„ ๋–จ์–ด๋œจ๋ฆด ์ˆ˜ ์žˆ๋Š” ๋ˆ„๊ตฌ๋“ ์ด๋‹ค.

์ด์— ๋Œ€ํ•ด ํ•˜๋Š” ์ผ: ๋ชจ๋“  ๋ฌธ์„œ๋Š” framedDocument(src/application/llm/documentParts.ts)๊ฐ€ ๊ฐ์‹ธ๋ฉฐ, ํŒŒ์ผ์˜ ์ด๋ฆ„์„ ๋ฐํžˆ๊ณ  ์–ด๋””์„œ ๋๋‚˜๋Š”์ง€๋ฅผ ํ‘œ์‹œํ•˜๊ณ  ๊ทธ ๊ตฌ๊ฐ„์„ ๋ฐ์ดํ„ฐ๋กœ ๋‹ค๋ฃจ๊ณ  ๊ฒฐ์ฝ” ์ง€์‹œ๋กœ ๋‹ค๋ฃจ์ง€ ๋ง๋ผ๊ณ  ๋ชจ๋ธ์—๊ฒŒ ๋งํ•œ๋‹ค. ์ด๋ฆ„์€ JSON ์ด์Šค์ผ€์ดํ”„๋˜๋ฏ€๋กœ ์กฐ์ž‘๋œ ํŒŒ์ผ๋ช…์ด ๋ ํ‘œ์‹œ๋ฅผ ์œ„์กฐํ•  ์ˆ˜ ์—†๋‹ค.

๊ทธ๊ฒƒ์€ ์™„ํ™”์ด์ง€ ํ•ด๊ฒฐ์ด ์•„๋‹ˆ๋‹ค. ์–ด๋–ค ๋ฌธ๊ตฌ๋„ ์ฃผ์ž…๋œ ํ…์ŠคํŠธ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋งŒ๋“ค์ง€ ๋ชปํ•˜๊ณ , ๋ฉ”์‹œ์ง€ ๋ณธ๋ฌธ์€ ์ด๋ฏธ ์‹ ๋ขฐ๋˜์ง€ ์•Š์•˜๋‹ค. agent ์˜ ๊ถŒํ•œ์„ ๊ทธ์— ๋งž์ถฐ ์žก์•„๋ผ. ์ฒจ๋ถ€๋ฅผ ์ฝ๋Š” agent ๋Š” ํŒŒ์ผ์„ ๋“  ๋‚ฏ์„  ์‚ฌ๋žŒ์—๊ฒŒ ์ฃผ์ง€ ์•Š์„ ๊ถŒํ•œ์„ ์ฅ๊ณ  ์žˆ์–ด์„œ๋Š” ์•ˆ ๋œ๋‹ค.

์•Œ์•„ ๋‘˜ ๋งŒํ•œ ๋‹ค๋ฅธ ์†์„ฑ๋“ค:

  • ๋ฌธ์„œ๋Š” ์ฝํž ๋ฟ, ์‹คํ–‰๋˜๊ฑฐ๋‚˜ ๋ Œ๋”๋ง๋˜์ง€ ์•Š๋Š”๋‹ค. ์ถ”์ถœ์€ ํ…์ŠคํŠธ๋งŒ ๋‚ด๋†“๋Š”๋‹ค. HTML ์€ ๋งˆํฌ์—…๊ณผ ํ™œ์„ฑ ๋‚ด์šฉ์„ ์ œ๊ฑฐํ•œ ํ…์ŠคํŠธ๋กœ ์ฝํžˆ๋ฉฐ, ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜ ์™ธ๋ถ€ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ์•Š๋Š”๋‹ค.
  • ๋ฌธ์„œ๋ฅผ ๋Œ€์‹ ํ•ด ๋ฌด์–ธ๊ฐ€๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์€ ์—†๋‹ค. ์ฒจ๋ถ€ ์•ˆ์˜ URL ์€ ๋‹ค๋ฅธ ๊ฒƒ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํ…์ŠคํŠธ์ผ ๋ฟ์ด๋‹ค. Agent๊ฐ€ ๋ฐ”์ธ๋”ฉํ•œ ๋„๊ตฌ๋งŒ์ด ๊ทธ๊ฒƒ์— ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๊ณ , ๊ทธ ๋„๊ตฌ ์ž์‹ ์˜ ๊ฐ€๋“œ ์•„๋ž˜์—์„œ ๊ทธ๋ ‡๋‹ค.
  • ํ…์ŠคํŠธ๋Š” PII ํ•„ํ„ฐ๋ฅผ ์ง€๋‚œ๋‹ค. Agent๊ฐ€ ์˜ตํŠธ์ธํ•˜๋ฉด ํ„ด์˜ ๋‚˜๋จธ์ง€์™€ ๋งˆ์ฐฌ๊ฐ€์ง€์ด๊ณ , ํ•œ๊ณ„๋„ ๊ฐ™๋‹ค (email, ์ „ํ™”๋ฒˆํ˜ธ, ํ•œ๊ตญ ๋“ฑ๋ก๋ฒˆํ˜ธ, ์นด๋“œ ๋ฒˆํ˜ธ, ์ด๋ฆ„์€ ์•„๋‹ˆ๋‹ค).
  • Chat์€ ์›๋ณธ๊ณผ ์ถ”์ถœ๋ฌธ์„ ๋ถ„๋ฆฌํ•ด ๋ณด๊ด€ํ•œ๋‹ค. ํ„ด๋‹น ์ตœ๋Œ€ 40,000์ž์˜ ์ถ”์ถœ๋ฌธ๊ณผ ํŒŒ์ผ ์ฐธ์กฐ๋Š” ์†Œ์œ ์ž ์ „์šฉ chat ํ–‰์— ๋‚จ๊ณ , ์›๋ณธ์€ source: attachment์ธ artifact๋กœ ์ €์žฅํ•œ๋‹ค. ์›๋ณธ์€ ๊ธฐ์กด artifact์™€ ๊ฐ™์€ ์ƒ์„ฑ์žยทAgent ์†Œ์œ ์ž ์ฝ๊ธฐ ์ •์ฑ…, artifact ๋ณด์กด ๊ธฐ๊ฐ„๊ณผ ์˜ค๋ธŒ์ ํŠธ ์ ‘๊ทผ ๋ชจ๋“œ๋ฅผ ๋”ฐ๋ฅธ๋‹ค. ์›๋ณธ์€ chat ์‚ญ์ œ๋งŒ์œผ๋กœ ์‚ญ์ œ๋˜์ง€ ์•Š์œผ๋ฉฐ artifact ์‚ญ์ œยท๋ณด์กด ์ •์ฑ…์ด ์†Œ์œ ํ•œ๋‹ค.

๋ฐ์ดํ„ฐ ๋…ธ์ถœ๊ณผ ๋ณด์กด

  • Trace ๋Š” ์ œํ•œ๋œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋งŒ ์ €์žฅํ•œ๋‹ค. ๋ฌธ์ž ์ˆ˜, token, ๋น„์šฉ, ์†Œ์š” ์‹œ๊ฐ„, subagent ์˜ trace id. ์›๋ณธ ํ”„๋กฌํ”„ํŠธ์™€ ๋„๊ตฌ ๊ฒฐ๊ณผ๋Š” ์˜์†๋˜์ง€ ์•Š์ง€๋งŒ, trace ์˜ error ์™€ warnings ๋Š” ์‹คํŒจ ํ…์ŠคํŠธ๋ฅผ ๊ทธ๋Œ€๋กœ ์ตœ๋Œ€ 1,000์ž๊นŒ์ง€ ๋ณด๊ด€ํ•˜๊ณ , ์ œ๊ณต์ž๋‚˜ ๋„๊ตฌ์˜ ์—๋Ÿฌ ๋ฌธ์ž์—ด์€ ๋‚ด์šฉ์„ ํ’ˆ์„ ์ˆ˜ ์žˆ๋‹ค.
  • ๋Ÿฐ์˜ ์ถ”๋ก ์€ ์ผ  Agent์—์„œ ๋ณต์›๋œ ์ฑ„๋กœ ์ €์žฅ๋œ๋‹ค. parameters.reasoningTrace ๋ฅผ ์ผ  Agent๋Š” assistant ๋ฉ”์‹œ์ง€์— ๊ทธ ๋Ÿฐ์˜ ์‚ฌ๊ณ ๋ฅผ ๋‚จ๊ธฐ๋Š”๋ฐ(AssistantChatMessage.reasoning, ๋ฉ”์‹œ์ง€๋‹น ์ตœ๋Œ€ 40,000๋ฐ”์ดํŠธ), ๊ทธ๊ฒƒ์€ content ์™€ ๊ฐ™์€ ์ถœ์ฒ˜. ์ฆ‰ ๋งˆ์Šคํ‚น์ด ํ’€๋ฆฐ ํ…์ŠคํŠธ๋‹ค. PII ํ•„ํ„ฐ๋Š” ๋ชจ๋ธ์ด ๋ณด๋Š” ๊ฒƒ์„ ์ œํ•œํ•˜์ง€ ์ฝ˜์†”์— ์ €์žฅ๋˜๋Š” ๊ฒƒ์„ ์ œํ•œํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ(content ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋‹ค), piiFiltering ์„ ์ผ  Agent์—์„œ๋„ ์ถ”๋ก ์€ ๊ฑธ๋Ÿฌ์ง€์ง€ ์•Š์€ ์ฑ„ chat ํ–‰์— ์•‰๋Š”๋‹ค. ์ถ”๋ก ์€ ์š”์ฒญ์„ ๋ชจ๋ธ ์ž์‹ ์˜ ๋ง๋กœ ๋˜ํ’€์ดํ•˜๋Š” ์ž๋ฆฌ๋ผ ์ž…๋ ฅ์ด ์‹ค์–ด ์˜จ ๊ฒƒ์„ ๊ทธ๋Œ€๋กœ ํ’ˆ๊ธฐ ์‰ฝ๋‹ค. ์˜ตํŠธ์ธ์ธ ์ด์œ ๊ฐ€ ์ด๊ฒƒ์ด๊ณ , ๋ณด์กด ๊ธฐ๊ฐ„์€ chat ํ–‰๊ณผ ๊ฐ™๋‹ค(RETENTION.chatDays).
  • /api/metrics ๋Š” agent, ์‚ฌ์šฉ์ž, model ์„ ์ง€๋ชฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ฉ”ํŠธ๋ฆญ ๋ผ๋ฒจ์€ ํžˆ์Šคํ† ๊ทธ๋žจ์˜ le ์™€ build ์ •๋ณด์˜ ์œ ํ•œํ•œ versionยทstage๋ฟ์ด๋‹ค.
  • ๋กœ๊ทธ ๋ผ์ธ์€ ๋Ÿฐ์˜ correlation id ๋ฅผ ์‹ค์„ ๋ฟ, ํ”„๋กฌํ”„ํŠธ ๋‚ด์šฉ์€ ๊ฒฐ์ฝ” ์‹ฃ์ง€ ์•Š๋Š”๋‹ค.
  • Trace, usage ํ–‰, chat, trigger ๋ฐฐ๋‹ฌ ํ–‰์€ ๋ชจ๋‘ expiresAt ์„ ์ง€๋‹ˆ๊ณ  schedule-scan ํ‹ฑ์˜ sweep ์ด ์ง€์šด๋‹ค. scan ํ˜ธ์ถœ์ด ์—†๋Š” ๋ฐฐํฌ์—์„œ๋Š” ์ด DB sweep์ด ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. OPERATIONS.md ์ฐธ๊ณ .
  • ๋ฉ”์‹œ์ง• ํŒŒ์ผ ์ฐธ์กฐ ๊ธฐ๋ก์€ SlackยทTelegramยทTeams์—์„œ ๋Œ€ํ™”ยทactor๋ณ„๋กœ ์ƒ์„ฑ ํŒŒ์ผ ID์™€ ์ด๋ฆ„๋งŒ 7์ผ๊ฐ„ ๋ณด๊ด€ํ•œ๋‹ค. URL์ด๋‚˜ ๋ฐ”์ดํŠธ๋Š” ๊ธฐ๋กํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ตœ๊ทผ 20๊ฐœ ๊ธฐ๋ก, ๊ธฐ๋ก๋‹น 20๊ฐœ ํŒŒ์ผ, ๋Ÿฐ๋‹น 20,000์ž๋กœ ์ œํ•œํ•˜๋ฉฐ ์ฐธ์กฐ๋ฅผ ์–ป์–ด๋„ File ๋„๊ตฌ์˜ ๊ถŒํ•œ ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•ด์•ผ ํ•œ๋‹ค.
  • TelegramยทTeams ๋Œ€ํ™” ํŠธ๋žœ์Šคํฌ๋ฆฝํŠธ ๋Š” agent ์˜ ๋ด‡๊ณผ ์ฃผ๊ณ ๋ฐ›์€ ๋ชจ๋“  ํ„ด์˜ ํ…์ŠคํŠธ ๋ฅผ 7์ผ๊ฐ„ ๋ณด๊ด€ํ•œ๋‹ค. ๋Œ€ํ™”๋ณ„๋กœ ์งˆ๋ฌธ๊ณผ ๋‹ต์„. ๋‘ ํ”Œ๋žซํผ ๋ชจ๋‘ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๋Œ๋ ค์ฃผ์ง€ ์•Š์•„ ํ›„์† ์งˆ๋ฌธ์ด ๊ทธ ์•ž์˜ ์งˆ๋ฌธ์„ ์‹ค์–ด ๋‚ ๋ผ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๊ฒƒ์€ chat ๋ฉ”์‹œ์ง€์ฒ˜๋Ÿผ ์ €์žฅ๋œ ์‚ฌ์šฉ์ž ํ…์ŠคํŠธ๋‹ค. chat ๊ณผ ๋‹ฌ๋ฆฌ ๊ทธ ๋Œ€ํ™”์˜ ๋‹ค์Œ ๋Ÿฐ ์™ธ์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ๊ทธ๊ฒƒ์„ ์ฝ์ง€ ์•Š๋Š”๋‹ค. ๋ณด๋‚ธ ์‚ฌ๋žŒ์˜ ํ”Œ๋žซํผ ์‚ฌ์šฉ์ž id(Telegram user id, Teams ๋Š” Entra object id)๋Š” ํ„ด ์˜†์— ์ €์žฅ๋˜๊ณ , ๋ณด๋‚ธ ์‚ฌ๋žŒ์˜ ์ด๋ฆ„ ์€ Agent๊ฐ€ callerContext ์— ์˜ตํŠธ์ธํ–ˆ์„ ๋•Œ๋งŒ ์ €์žฅ๋˜๋ฉฐ, ์˜ตํŠธ์ธ์„ ๋ˆ Agent๋Š” ์ด์ „์— ์ €์žฅ๋œ ์ด๋ฆ„๋„ ์ฝ์ง€ ์•Š๋Š”๋‹ค. ํ–‰์€ agent ํŒŒํ‹ฐ์…˜์— ์žˆ์–ด agent ๋ฅผ ์ง€์šฐ๋ฉด ํ•จ๊ป˜ ์ง€์›Œ์ง„๋‹ค.
  • ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€ ๋Š” S3_BUCKET_NAME ์ด ์„ค์ •๋ผ ์žˆ์œผ๋ฉด ์ถ”์ธกํ•  ์ˆ˜ ์—†๋Š” UUID ํ‚ค ์•„๋ž˜ ์ €์žฅ๋˜๊ณ , chat ํ–‰์€ ์ฃผ์†Œ๊ฐ€ ์•„๋‹ˆ๋ผ ์˜ค๋ธŒ์ ํŠธ ํ‚ค ๋ฅผ ๋ณด๊ด€ํ•œ๋‹ค. ์ฝ๊ธฐ ์‹œ์ ์— ํ‚ค๊ฐ€ ์ฃผ์†Œ๊ฐ€ ๋˜๋ฉฐ, ์ˆ˜๋ช…์€ ๋…์ž์— ๋งž์ถฐ ๊ณ ๋ฅธ๋‹ค. chat ๋ทฐ์—๋Š” 15๋ถ„, ๋ฉ”์‹œ์ง• ๋‹ต๋ณ€์— ํฌํ•จ๋œ ๋งํฌ์—๋Š” 7์ผ(SigV4 pre-sign ์˜ ์ƒํ•œ์ด๊ณ , proxied ํ† ํฐ๋„ ๊ฐ™์€ ๊ฐ’์„ ์“ด๋‹ค). ๊ทธ ๋งํฌ๋Š” ๊ทธ๊ฒƒ์ด ํ•จ๊ป˜ ์˜จ ๋‹ต์„ ์ด๋ฏธ ์ฝ์„ ์ˆ˜ ์žˆ๋˜ ์ฒญ์ค‘์ด ์ฅ”๋‹ค(src/shared/artifactUrlTtl.ts). ์ฃผ์†Œ์˜ ๋ชจ์–‘ ์€ ARTIFACT_ACCESS_MODE ๊ฐ€ ์ •ํ•œ๋‹ค:
    • proxied. ์Šคํ† ์–ด๋Š” ์•ฑ์—๊ฒŒ๋งŒ ๋‹ฟ๊ณ  ๋…์ž๋Š” ์•ฑ์˜ ์ฃผ์†Œ PUBLIC_BASE_URL/api/objects/<key>?exp=<unix>&sig=<hmac>[&dl=<filename>] ๋ฅผ ๋ฐ›๋Š”๋‹ค (src/infrastructure/storage/objectUrlToken.ts). ๊ทธ ๋ผ์šฐํŠธ๋Š” ์„ธ์…˜์„ ์š”๊ตฌํ•˜์ง€ ์•Š์œผ๋ฉฐ ๊ทธ๊ฒƒ์ด ๊ณ„์•ฝ์ด๋‹ค: ์ฃผ์†Œ๋ฅผ ์ฅ๋Š” ๊ฒƒ์€ <img> ํƒœ๊ทธ์™€ Slack ๋ฉ”์‹œ์ง€๋ผ ์ฟ ํ‚ค๋ฅผ ๋‚ผ ์ˆ˜ ์—†๋‹ค. ํ† ํฐ์ด ์ž๊ฒฉ ์ฆ๋ช…์ด๋‹ค. ํ‚คยท๋งŒ๋ฃŒยทํŒŒ์ผ๋ช…์„ ํ•จ๊ป˜ ๋ฎ๋Š” HMAC-SHA256 ์ด๊ณ , ์„œ๋ช… ํ‚ค๋Š” AES_ENCRYPTION_KEY ์—์„œ HKDF(agent-studio/object-url/v1)๋กœ ํŒŒ์ƒ๋˜์–ด ๊ทธ ๋ฐ”์ดํŠธ๊ฐ€ ์ €์žฅ๋œ ํ† ํฐ์„ ์•”ํ˜ธํ™”ํ•˜๋Š” ๋ฐ”์ดํŠธ์™€ ๊ฒฐ์ฝ” ๊ฐ™์ง€ ์•Š๋‹ค. ์ฆ๋ช…ํ•˜๋Š” ๊ฒƒ์€ ์ด ๋ฐฐํฌ๊ฐ€ ์ด ํ‚ค์— ๋Œ€ํ•ด ์ด ์ˆ˜๋ช…๊ณผ ์ด ํŒŒ์ผ๋ช…์œผ๋กœ ๋ฐœํ–‰ํ–ˆ๋‹ค ๋Š” ์‚ฌ์‹ค๋ฟ์ด๋‹ค. ํŒŒ์ผ๋ช…์€ ์žฅ์‹์ด ์•„๋‹ˆ๋ผ ์„œ๋ช…์— ๋ฌถ์—ฌ ์žˆ์–ด dl ์„ ๋–ผ์–ด ๋‹ค์šด๋กœ๋“œ๋ฅผ ์ธ๋ผ์ธ ๋ณด๊ธฐ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†๊ณ , ๋งŒ๋ฃŒ์™€ ์„œ๋ช… ๋ถˆ์ผ์น˜๋Š” ํ•˜๋‚˜์˜ 403 ์œผ๋กœ ๋‹ตํ•œ๋‹ค(๋‘˜์„ ๊ตฌ๋ณ„ํ•ด ์ฃผ๋ฉด ์œ„์กฐ์ž์—๊ฒŒ ์–ด๋А ์ชฝ์„ ๊ณ ์น ์ง€ ์•Œ๋ ค ์ค€๋‹ค). ์—†์–ด์ง„ ์˜ค๋ธŒ์ ํŠธ๋Š” 404, ์ฝ๊ธฐ๋Š” MAX_PROXIED_OBJECT_BYTES ์—์„œ ๋Š๊ธฐ๋ฉฐ, Cache-Control: private ์˜ max-age ๋Š” ํ† ํฐ์˜ ๋‚จ์€ ์ˆ˜๋ช…์„ ๋„˜์ง€ ์•Š๋Š”๋‹ค. ๋ฐ”์ดํŠธ๋Š” ์ฝ˜์†”์˜ origin ์—์„œ ๋‚˜๊ฐ€๋ฏ€๋กœ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ฌธ์„œ๋กœ ๋ Œ๋”ํ•  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…(HTML, SVG, ํ…์ŠคํŠธ)์€ /view ์™€ ๊ฐ™์€ sandbox; default-src 'none' ์•„๋ž˜ ๋ถˆํˆฌ๋ช… origin ์— ๋†“์ด๊ณ , ๋ž˜์Šคํ„ฐ ์ด๋ฏธ์ง€์™€ PDF ๋งŒ frame-ancestors 'none' ์œผ๋กœ ๋๋‚œ๋‹ค. next.config.ts ๊ฐ€ ์ด ์ฃผ์†Œ๋ฅผ ์ฝ˜์†” ํ—ค๋”์—์„œ ์ œ์™ธํ•˜๋Š” ์ด์œ ๊ฐ€ ๊ทธ๊ฒƒ์ด๋‹ค.

    • authenticated. ์Šคํ† ์–ด์˜ pre-signed URL. ์Šคํ† ์–ด๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋‹ฟ์•„์•ผ ํ•˜๊ณ  ๋ฒ„ํ‚ท์€ ๋น„๊ณต๊ฐœ๋กœ ๋‚จ๋Š”๋‹ค.

    • public. ์˜๊ตฌ์ ์ธ ์ง์ ‘ URL. ๋ช…์‹œ์ ์ธ ๊ณต๊ฐœ ์ฝ๊ธฐ ๋ฒ„ํ‚ท ์ •์ฑ…์„ ์š”๊ตฌํ•˜๊ณ  URL ์„ ์–ป์€ ๋ˆ„๊ตฌ์—๊ฒŒ๋‚˜ ๋ฐ”์ดํŠธ๋ฅผ ๋…ธ์ถœํ•œ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ธ์ฆ์€ ์—ฌ์ „ํžˆ ๊ฐค๋Ÿฌ๋ฆฌ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์™€ ์‚ญ์ œ๋ฅผ ๋ณดํ˜ธํ•˜์ง€๋งŒ ์˜ค๋ธŒ์ ํŠธ๋Š” ๋ณดํ˜ธํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ฃผ์†Œ๊ฐ€ ๋ณด๊ธฐ๊ฐ€ ์•„๋‹ˆ๋ผ ๋‹ค์šด๋กœ๋“œ์ธ ๊ณณ์—์„œ๋Š” ์–ด์จŒ๋“  ๋ฏธ๋ฆฌ ์„œ๋ช…ํ•œ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ €์žฅํ•  ํŒŒ์ผ ์ด๋ฆ„์€ ResponseContentDisposition ์ด ์‹ค์–ด ๋‚˜๋ฅด๋Š”๋ฐ S3 ๋Š” ์ต๋ช… GET ์—์„œ ๊ทธ๊ฒƒ์„ ๊ฑฐ๋ถ€ํ•˜๋ฏ€๋กœ, ์„œ๋ช…๋˜์ง€ ์•Š์€ ๋งํฌ๋Š” ์˜ค๋ธŒ์ ํŠธ๋ฅผ UUID ํ‚ค๋กœ๋งŒ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.

    • ๋ ˆ๊ฑฐ์‹œ ํ–‰์€ ๊ณต๊ฐœ url ์„ ์ง€๋‹ˆ๊ณ  ์žˆ์„ ์ˆ˜ ์žˆ๊ณ  ๊ทธ๋Œ€๋กœ ๋˜์ฝํžŒ๋‹ค. ๊ทธ๊ฒƒ์„ ๋‹ค์‹œ ์“ด๋‹ค๊ณ  ํ•ด์„œ ์ด๋ฏธ ๊ณต๊ฐœ์ธ ๊ทธ ์˜ค๋ธŒ์ ํŠธ๋“ค์— ๋ˆ„๊ฐ€ ๋‹ฟ์„ ์ˆ˜ ์žˆ๋Š”์ง€๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค. ๊ณต๊ฐœ ๋…ธ์ถœ ์—ฌ๋ถ€๋Š” ํ˜„์žฌ bucket policyยทACL์ด ๊ฒฐ์ •ํ•œ๋‹ค. ์•ฑ์˜ metadata๋‚˜ access mode๋งŒ ๋ฐ”๊ฟ”์„œ๋Š” ๊ธฐ์กด ๊ฐ์ฒด๋ฅผ ๋น„๊ณต๊ฐœ๋กœ ์ „ํ™˜ํ•˜์ง€ ๋ชปํ•œ๋‹ค. ์ •์ฑ…์„ ๋‹ซ์œผ๋ฉด ์˜› ๊ณต๊ฐœ URL๋„ ๋”๋Š” ์ ‘๊ทผ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค.

    • ํŽ˜์ด์ง€๋Š” ์˜ค๋ธŒ์ ํŠธ ์ฃผ์†Œ๋กœ ๋‚˜๊ฐ€์ง€ ์•Š๋Š”๋‹ค. SAVABLE_TYPES ์— ํฌํ•จ๋œ, ์‚ฌ๋žŒ์ด ์ฝ๋„๋ก ๋งŒ๋“  artifact ๋งŒ /view ๋ฅผ ํ†ตํ•ด ์•ฑ์ด ๋ฐ”์ดํŠธ๋กœ ๋‹ตํ•œ๋‹ค. ์„œ๋ช…ํ•œ ์˜ค๋ธŒ์ ํŠธ URL ์€ sandbox ํ—ค๋”๋ฅผ ์‹ค์„ ์ˆ˜ ์—†๊ณ , ํ•œ ๋ฒˆ ๊ฑด๋„ค์ง€๋ฉด ๊ทธ๊ฒƒ์„ ์—ฐ ์‚ฌ๋žŒ์˜ ๊ถŒํ•œ๋ณด๋‹ค ์˜ค๋ž˜ ์‚ด๋ฉฐ, public ๋ชจ๋“œ์—์„œ๋Š” ์˜๊ตฌ๋‹ค. ์ž„์˜์˜ ๋งˆํฌ์—…์ด ์‹คํ–‰๋˜๋Š” ์˜๊ตฌ ์ฃผ์†Œ๋Š” ์ €์žฅ์†Œ๊ฐ€ ์•„๋‹ˆ๋ผ ํ˜ธ์ŠคํŒ…์ด๋‹ค. /view ๋Š” ์‚ญ์ œ์™€ ๊ฐ™์€ ์กฐ๊ฑด์œผ๋กœ ๋งค ์š”์ฒญ์„ ์ธ๊ฐ€ํ•˜๊ณ , Content-Security-Policy ์˜ sandbox ๋กœ ๋ฌธ์„œ๋ฅผ ๋ถˆํˆฌ๋ช… ์˜ค๋ฆฌ์ง„์— ๋†“๋Š”๋‹ค. HTML ์›๋ฌธ์€ ๋ณ„๋„์˜ iframe์—๋งŒ ๋“ค์–ด๊ฐ€๊ณ  ์ฝ˜์†”์˜ ์ฟ ํ‚คยท์Šคํ† ๋ฆฌ์ง€ยทDOM์— ๋‹ฟ์ง€ ๋ชปํ•œ๋‹ค. nosniff์™€ no-referrer๋„ ํ•จ๊ป˜ ์ ์šฉํ•œ๋‹ค.

    • HTML์€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ์—ฌ๋Š” ์ฆ‰์‹œ ๊ฒฉ๋ฆฌ๋œ iframe์—์„œ ์‹คํ–‰ํ•œ๋‹ค. ์›๋ฌธ์„ ์„ ์–ธ๋œ charset์œผ๋กœ ์—„๊ฒฉํ•˜๊ฒŒ ๋””์ฝ”๋”ฉํ•˜๊ณ , ์ „์ฒด ๋‚ด์šฉ์„ ์ด์Šค์ผ€์ดํ”„ํ•œ srcdoc ์†์„ฑ์œผ๋กœ ์ „๋‹ฌํ•œ๋‹ค. ๋ฐ”๊นฅ ํ™”๋ฉด์—๋Š” ์ œํ’ˆ์˜ ์ œ์–ด ์ฝ”๋“œ๋งŒ ์‹คํ–‰๋˜๋ฉฐ, ์›๋ฌธ scriptยท์ด๋ฒคํŠธยทcanvasยทSVG๋Š” iframe ์•ˆ์—์„œ ๋ณด์กดํ•œ๋‹ค. HTTP CSP์™€ iframe ๋ชจ๋‘ sandbox allow-scripts๋ฅผ ์ ์šฉํ•˜๊ณ  allow-same-origin, top-navigation, popup, form, download ๊ถŒํ•œ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค. iframe์„ ์ œ๊ฑฐํ•˜๋ฉด ์‹คํ–‰ ์ƒํƒœ๋ฅผ ๋ฒ„๋ฆฌ๊ณ , ๋‹ค์‹œ ์‹œ์ž‘ํ•˜๋ฉด ์›๋ณธ์„ ์ƒˆ iframe์— ๋„ฃ๋Š”๋‹ค. ์ฝ”๋“œ ์˜ค๋ฅ˜๋‚˜ ์ƒํƒœ๋ฅผ ์ €์žฅ ์„ฑ๊ณต๊ณผ ํ˜ผ๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค.

    • ๊ฒฉ๋ฆฌ์™€ ํ†ต์‹  ์ œํ•œ์˜ ๋ฒ”์œ„๋ฅผ ๊ตฌ๋ถ„ํ•œ๋‹ค. HTML์˜ INTERACTIVE_HTML_VIEW_POLICY๋Š” fetch/XHR/WebSocket, ์™ธ๋ถ€ scriptยทCSSยทimageยทfont, worker, ์™ธ๋ถ€ ํ”„๋ ˆ์ž„ ์ด๋™๊ณผ ํผ ์ œ์ถœ์„ ์ œํ•œํ•œ๋‹ค. frame-src 'none'์€ inline srcdoc์„ ํ—ˆ์šฉํ•˜๋ฉด์„œ ๋„คํŠธ์›Œํฌ frame ํƒ์ƒ‰์„ ๋ง‰๋Š”๋‹ค. ๋‚ด๋ถ€ ๋ชฉ์ฐจ ๋งํฌ๋Š” ๊ธฐ์ค€ URL์„ about:srcdoc์œผ๋กœ ์ง€์ •ํ•ด ์ฒ˜๋ฆฌํ•˜๋ฉฐ, base-uri about:์œผ๋กœ ์™ธ๋ถ€ ๊ธฐ์ค€ URL์„ ๊ฑฐ์ ˆํ•œ๋‹ค. ์นด๋ฉ”๋ผยท๋งˆ์ดํฌยท์œ„์น˜ ๋“ฑ์€ Permissions Policy๋กœ ์ œํ•œํ•œ๋‹ค. ์ด ์ •์ฑ…์€ WebRTC ๋“ฑ ๋ชจ๋“  ๋ธŒ๋ผ์šฐ์ € ํ†ต์‹ ์ด๋‚˜ CPUยท๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ฐ•์ œ ๊ฒฉ๋ฆฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์—ด๊ธฐ๋Š” HTML ์ฝ”๋“œ ์‹คํ–‰์„ ํฌํ•จํ•˜๋ฏ€๋กœ ์‹ ๋ขฐํ•˜๋Š” ํŒŒ์ผ์„ ๋‹ค๋ฃฌ๋‹ค๋Š” ์ „์ œ๋กœ ์‚ฌ์šฉํ•œ๋‹ค. ์™„์ „ํ•œ ๋ฌดํ†ต์‹ ยท์ž์› ๊ฒฉ๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋ฉด ๋ณ„๋„์˜ ์‹คํ–‰ ํ™˜๊ฒฝ์ด ํ•„์š”ํ•˜๋‹ค. iframe์—์„œ ๋ณด๊ณ ํ•œ ์˜ค๋ฅ˜๋Š” ํ˜„์žฌ iframe์˜ Window๋ฅผ ํ™•์ธํ•œ ๋’ค ๊ณ ์ • ์•ˆ๋‚ด๋กœ๋งŒ ํ‘œ์‹œํ•œ๋‹ค. ์ฐจ๋‹จ๋œ ๋ฆฌ์†Œ์Šค์™€ ์™ธ๋ถ€ ์ด๋™๋„ ๊ณ ์ • ์•ˆ๋‚ด๋กœ ํ‘œ์‹œํ•˜๊ณ , ์Šคํฌ๋ฆฝํŠธ ์˜ค๋ฅ˜๋ฅผ ์šฐ์„ ํ•œ๋‹ค. ์˜ค๋ฅ˜ ์›๋ฌธยท์ฃผ์†Œ๋ฅผ ๋ถ€๋ชจ ํ™”๋ฉด์— ๋ Œ๋”ํ•˜๊ฑฐ๋‚˜ ์„œ๋ฒ„๋กœ ๋ณด๋‚ด์ง€ ์•Š์œผ๋ฉฐ ํŠน๊ถŒ APIยท์ธ์ฆ ์ •๋ณด๋Š” ์ „๋‹ฌํ•˜์ง€ ์•Š๋Š”๋‹ค.

    • ์ •์  view๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. ARTIFACT_VIEW_POLICY์—๋Š” ์–ด๋–ค allow-*๋„ ๋ถ™์ด์ง€ ์•Š๋Š”๋‹ค. Markdown์€ raw HTML์„ ํ…์ŠคํŠธ๋กœ ๋‚ด๋ณด๋‚ด๊ณ , CSV๋Š” ์ด์Šค์ผ€์ดํ”„๋œ ํ‘œ, SVG๋Š” <img>๋กœ ํ‘œ์‹œํ•œ๋‹ค. HTML ์‹คํ–‰ ์ •์ฑ…์€ ์ด ๊ฒฝ๋กœ์— ์ ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

    • ์ „์—ญ ํ—ค๋”์™€์˜ ์ถฉ๋Œ์„ ๊ฒ€์‚ฌํ•œ๋‹ค. next.config.ts๋Š” ์ž์ฒด CSP๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋‘ ๊ฒฝ๋กœ๋ฅผ ์ œ์™ธํ•œ๋‹ค. ์ƒˆ ํŒŒ์ผ ์‘๋‹ต์ด๋‚˜ ์ •์ฑ… ๋ณ€๊ฒฝ๋„ ์‘๋‹ต ํ—ค๋”์˜ ๊ฒฝ๊ณ„๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค.

    • ์ผ๋ฐ˜ ๋Ÿฐ Artifact์˜ object ๋งŒ๋ฃŒ๋Š” ์ €์žฅ์†Œ ์ •์ฑ…์ด ๋‹ด๋‹นํ•œ๋‹ค. ํ‹ฑ์€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ–‰์„ ์ง€์šฐ๋ฉฐ artifacts/์˜ bytes์—๋Š” ๋ณ„๋„ lifecycle์„ ์ ์šฉํ•œ๋‹ค. ๋น„๊ณต๊ฐœ source-files/๋Š” worker๊ฐ€ ํŒŒ์ผ๋ณ„ ๋ณด์กด ๊ธฐํ•œ์„ ๊ฒ€์‚ฌํ•˜๊ณ  ๋ณธ๋ฌธ์„ ์‚ญ์ œ ํ‘œ์‹์œผ๋กœ ๊ต์ฒดํ•˜๋ฏ€๋กœ ๊ฐ™์€ ์ผ๊ด„ ๋งŒ๋ฃŒ ๊ทœ์น™์„ ์ ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. INSTALL.md์˜ ์ €์žฅ์†Œ ์ •์ฑ…์„ ๋”ฐ๋ฅธ๋‹ค.

  • ๋น„๊ณต๊ฐœ ํŒŒ์ผ Artifacts๋Š” ๊ฐ™์€ S3_BUCKET_NAME์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ ์ผ๋ฐ˜ object ์ ‘๊ทผ ๊ฒฝ๋กœ๋กœ ์ฝ๊ฑฐ๋‚˜ URL์„ ๋ฐœ๊ธ‰ํ•  ์ˆ˜ ์—†๋‹ค. source-files/ ํ‚ค๋Š” ์ผ๋ฐ˜ ์–ด๋Œ‘ํ„ฐ์™€ bearer URL ๊ฒ€์ฆ์—์„œ ๊ฑฐ์ ˆํ•˜๋ฉฐ, ์†Œ์œ ์žยทAgentยทํŒŒ์ผ ์ƒํƒœยท๋งŒ๋ฃŒ๋ฅผ ํ™•์ธํ•˜๋Š” ๋‹ค์šด๋กœ๋“œยท๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ฒฝ๋กœ๋งŒ ์‚ฌ์šฉํ•œ๋‹ค. public ๋ชจ๋“œ์—์„œ๋Š” ๊ฐ€์ ธ์˜ค๊ธฐ ์ „์— ๋น„๊ณต๊ฐœ ํŒŒ์ผ ์“ฐ๊ธฐ๋ฅผ ๊ฑฐ์ ˆํ•œ๋‹ค. ์‹ค์ œ bucket policy์™€ ACL์˜ ์ต๋ช… ์ฝ๊ธฐ ์ฐจ๋‹จ์€ ๋ฐฐํฌ ํ™˜๊ฒฝ์ด ์†Œ์œ ํ•œ๋‹ค.
  • Artifact ํ–‰ ์€ ๋Ÿฐ์ด ๋งŒ๋“ค์–ด ๋‚ธ ๋ชจ๋“  ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ง€๋ชฉํ•˜๊ณ , ๊ทธ๊ฒƒ์ด ์ €์žฅ๋œ ์ด๋ฏธ์ง€๋‚˜ ๋ฌธ์„œ๋ฅผ ๋‚˜์—ดํ•˜๊ณ  ์ง€์šธ ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“œ๋Š” ์ „๋ถ€๋‹ค. ์–ธ๊ธ‰ํ•  ๋งŒํ•œ ๊ท€๊ฒฐ์ด ์…‹ ์žˆ๋‹ค:
    • ํ–‰์€ ๊ฐค๋Ÿฌ๋ฆฌ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋ ค๊ณ  ํ”„๋กฌํ”„ํŠธ์˜ 500์ž ๋ฐœ์ทŒ ๋ฅผ ๋ณด๊ด€ํ•œ๋‹ค. ๊ทธ๊ฒƒ์€ ARTIFACT_RETENTION_DAYS ๋™์•ˆ ์‚ฌ๋Š” ์‚ฌ์šฉ์ž ํ…์ŠคํŠธ์ด๋ฉฐ, ๊ทธ๊ฒƒ์„ ์‹ค์–ด ์˜จ chat ๋ฉ”์‹œ์ง€๋ณด๋‹ค ์˜ค๋ž˜ ๋‚จ๋Š”๋‹ค. PII ํ•„ํ„ฐ๋ง์€ ๋ชจ๋ธ ์ด ๋ณด๋Š” ๊ฒƒ์„ ํ•œ์ •ํ•  ๋ฟ, ์ €์žฅ๋˜๋Š” ๊ฒƒ์„ ํ•œ์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค.
    • agent ์˜ artifact ํƒญ์€ ๊ทธ Agent์˜ ์†Œ์œ ์ž๊ฐ€ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค. trace ๊ฐ€ ์“ฐ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๊ทœ์น™์ด๊ณ , ์ด์œ ๋„ ๊ฐ™๋‹ค(๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ๋Ÿฐํƒ€์ž„ ์ถœ๋ ฅ์„ ๋‹ด๊ณ  ์žˆ๋‹ค). ์‹ค์ œ๋กœ๋Š” trace ๋ณด๋‹ค ๋” ๋„“์€ ๋…ธ์ถœ์ด๋‹ค. Agent trace๋Š” ํ•ญ์ƒ ๊ธฐ๋กํ•˜๊ณ  ๊ธฐ๋ณธ 30์ผ์„ ๋ณด๊ด€ํ•˜์ง€๋งŒ, artifact ๋Š” ๋ชจ๋“  ์˜ค๋ธŒ์ ํŠธ์ด๊ณ  180์ผ์„ ๋ณด๊ด€ํ•œ๋‹ค.
    • artifact ๋ฅผ ์ง€์šฐ๋ฉด ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋จผ์ €, ํ–‰์„ ๋‚˜์ค‘์— ์ง€์šฐ๋ฏ€๋กœ ์ค‘๋‹จ๋œ ์‚ญ์ œ๋Š” ์žฌ์‹œ๋„๋กœ ์ˆ˜๋ ดํ•œ๋‹ค. chat ๋ฉ”์‹œ์ง€๋Š” ๊ทธ ํ‚ค์˜ ์‚ฌ๋ณธ์„ ์ž๊ธฐ ์•ˆ์— ๊ฐ–๊ณ  ์žˆ์œผ๋ฏ€๋กœ ํŠธ๋žœ์Šคํฌ๋ฆฝํŠธ๋Š” ๊ทธ ๋’ค๋กœ ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Œ์œผ๋กœ ๋ Œ๋”๋งํ•œ๋‹ค. ํ™•์ธ ๋ฌธ๊ตฌ๊ฐ€ ๊ทธ ์‚ฌ์‹ค์„ ๋ฏธ๋ฆฌ ๋งํ•ด ์ค€๋‹ค.

์šด์˜ ๋…ธํŠธ

  • ์œ ์ถœ๋œ ๊ฐœ์ธ ํ† ํฐ์€ ํ๊ธฐยท์žฌ๋ฐœ๊ธ‰ํ•œ๋‹ค. ๋ณธ์ธ APIยทWebhook ํ† ํฐ์€ Integrations ๋˜๋Š” DELETEยทPOST /api/agents/{name}/token ๋ฐ โ€ฆ/webhook-token์œผ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค. ์‹คํ–‰์€ ํ˜„์žฌ ๊ณ„์ •ยทcredential์„ ๋‹ค์‹œ ๊ฒ€์‚ฌํ•œ๋‹ค.
  • ์„ค์ • ์ „ํŒŒ๋Š” ์ฆ‰์‹œ๊ฐ€ ์•„๋‹ˆ๋‹ค. ๊ฐ•๋“ฑ๋œ admin์˜ ๊ถŒํ•œ ๋ณ€๊ฒฝ๋Š” ๊ทธ ์“ฐ๊ธฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์€ ์ธ์Šคํ„ด์Šค์—์„œ ์„ค์ • ์บ์‹œ๊ฐ€ ๋งŒ๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ณ„์† ๋™์ž‘ํ•œ๋‹ค(SETTINGS_CACHE_TTL_MS, ๊ธฐ๋ณธ 5์ดˆ). ์ธ์Šคํ„ด์Šค ๊ฐ„ ์ฆ‰์‹œ ์ทจ์†Œ์—๋Š” ๊ณต์œ  ๋ฌดํšจํ™” ์‹ ํ˜ธ๊ฐ€ ํ•„์š”ํ•œ๋ฐ, ์•„์ง ์—†๋‹ค. ์“ฐ๊ธฐ๋ฅผ ์ฒ˜๋ฆฌํ•œ ์ธ์Šคํ„ด์Šค๋Š” ์บ์‹œ ์„ธ๋Œ€๋ฅผ ์“ฐ๋ฏ€๋กœ, ์ด๋ฏธ ์ง„ํ–‰ ์ค‘์ด๋˜ ์ฝ๊ธฐ๊ฐ€ ๋ฌดํšจํ™”๋œ ํ•ญ๋ชฉ์„ ๋‹ค์‹œ ์ฑ„์šธ ์ˆ˜ ์—†๋‹ค.
  • ์ธ์ฆ rate limit ์€ ํด๋ผ์ด์–ธํŠธ IP ๋กœ ํ‚ค์ž‰ํ•˜๊ณ , ํ”„๋ก์‹œ ๋’ค์—์„œ ๊ทธ๊ฒƒ์€ TRUSTED_PROXY_CIDRS ๋กœ ํ•ด์„๋œ๋‹ค. CONFIGURATION.md ์ฐธ๊ณ . ํ”„๋ก์‹œ ๋‘˜ ๋’ค์—์„œ ๊ทธ๊ฒƒ์„ ๋น„์›Œ ๋‘๋ฉด ๋ชจ๋“  ์š”์ฒญ์ด ๊ฐ™์€ ํ™‰์œผ๋กœ ํ•ด์„๋ผ ํ•˜๋‚˜์˜ ๊ณต์œ  ๋ฒ„ํ‚ท์— ๋–จ์–ด์ง€๋ฏ€๋กœ, ๋ฆฌ๋ฏธํ„ฐ๋Š” ๋‚จ์šฉ์ž๊ฐ€ ์•„๋‹ˆ๋ผ ํ•จ๋Œ€ ์ „์ฒด๋ฅผ ์กฐ์ธ๋‹ค.
  • AWS ์ž๊ฒฉ ์ฆ๋ช…์€ AWS ๋ฅผ ์“ฐ๋Š” ๊ธฐ๋Šฅ์—๋งŒ ํ•„์š”ํ•˜๊ณ (Bedrock, AWS S3 ์ž์ฒด), ์—ญํ• ์ด๋‚˜ ํ‘œ์ค€ AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY ์Œ์œผ๋กœ ์˜จ๋‹ค. MinIO ๊ฐ™์€ S3 ํ˜ธํ™˜ ์Šคํ† ์–ด์˜ ์ž๊ฒฉ ์ฆ๋ช…์€ ๊ทธ๊ฒƒ๊ณผ ๋‹ค๋ฅธ S3_ACCESS_KEY_ID/S3_SECRET_ACCESS_KEY ์Œ์ด๋‹ค. ํ•œ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋‘˜ ๋‹ค ์“ธ ์ˆ˜ ์žˆ๋‹ค. ํ‚ค๋ฅผ ์ด๋ฏธ์ง€์— ๊ตฝ์ง€ ๋งˆ๋ผ.
  • AES_ENCRYPTION_KEY ๋ฅผ ํšŒ์ „ํ•˜๋ฉด ์ €์žฅ๋œ ์‹œํฌ๋ฆฟ๋งŒ์ด ์•„๋‹ˆ๋ผ proxied ์˜ค๋ธŒ์ ํŠธ ์ฃผ์†Œ๋„ ์ „๋ถ€ ๋ฌดํšจ๊ฐ€ ๋œ๋‹ค. ์„œ๋ช… ํ‚ค๊ฐ€ ๊ฑฐ๊ธฐ์„œ ํŒŒ์ƒ๋œ๋‹ค. ๋ฐœ๊ธ‰๋œ ๋งํฌ๊ฐ€ ๋ฌดํšจํ™”๋˜๊ณ  ๊ธฐ์กด credentialยทSDK SessionยทWorkspace checkpoint๋„ ์ƒˆ ํ‚ค๋กœ๋Š” ๋ณตํ˜ธํ™”ํ•  ์ˆ˜ ์—†๋‹ค. ํ‚ค ๊ต์ฒด๋งŒ์œผ๋กœ ๊ธฐ์กด ์•”ํ˜ธ๋ฌธ์„ ๋‹ค์‹œ ์•”ํ˜ธํ™”ํ•˜๋Š” ์ž๋™ ์ด๊ด€์€ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค.

There aren't any published security advisories