본문으로 이동

도움말:S3 연구 메모리/설치와 검증: 두 판 사이의 차이

S3 연구 메모리
S3 연구 메모리 저장소 문서 동기화
S3 연구 메모리 저장소 문서 동기화
101번째 줄: 101번째 줄:
원문 토큰은 필요한 사람에게 비공개 채널로 전달합니다. Git, 공개 이슈, 프롬프트, 스크린샷에는 넣지 마세요. 공용 토큰을 바꾸면 쓰기 클라이언트를 함께 갱신해야 합니다. 공개 조회에는 영향이 없습니다.
원문 토큰은 필요한 사람에게 비공개 채널로 전달합니다. Git, 공개 이슈, 프롬프트, 스크린샷에는 넣지 마세요. 공용 토큰을 바꾸면 쓰기 클라이언트를 함께 갱신해야 합니다. 공개 조회에는 영향이 없습니다.


설정 스크립트는 비어 있는 <code><nowiki>S3RM_WIKI_REVISION_HMAC_KEY</nowiki></code>에 URL-safe 무작위 32바이트 값을 만들고 이후 실행과 토큰 교체에서는 보존합니다. 스크립트를 쓰지 않는 설정은 <code><nowiki>openssl rand -hex 32</nowiki></code>로 직접 만들 수 있습니다. 이 키는 <code><nowiki>mcp-http</nowiki></code>와 MediaWiki 저장 훅만 공유하며 클라이언트에는 전달하지 않습니다. 공용 토큰이나 위키 비밀번호와 같은 값을 쓰지 마세요. 키가 없거나 형식이 맞지 않으면 MCP가 시작을 거부합니다.
설정 스크립트는 비어 있거나 예시인 <code><nowiki>S3RM_WIKI_BOT_PASSWORD</nowiki></code>에 32자리 API 전용 비밀번호를 만들고, 비어 있는 <code><nowiki>S3RM_WIKI_REVISION_HMAC_KEY</nowiki></code>에 URL-safe 무작위 32바이트 값을 만듭니다. 이후 실행과 토큰 교체에서는 둘 다 보존합니다. 스크립트를 쓰지 않는 설정은 <code><nowiki>openssl rand -hex 32</nowiki></code>로 직접 만들 수 있습니다. 이 키는 <code><nowiki>mcp-http</nowiki></code>와 MediaWiki 저장 훅만 공유하며 클라이언트에는 전달하지 않습니다. 공용 토큰이나 위키 비밀번호와 같은 값을 쓰지 마세요. 키가 없거나 형식이 맞지 않으면 MCP가 시작을 거부합니다.
 
같은 스크립트는 별도의 <code><nowiki>S3RM_MCP_PROXY_SECRET</nowiki></code>도 생성합니다. 이 값은 Caddy와 <code><nowiki>mcp-http</nowiki></code>만 공유하며, Caddy가 덮어쓴 client IP 헤더임을 확인하는 데만 씁니다. revision HMAC 키와 재사용하거나 클라이언트에 전달하지 않습니다.


Compose는 현재 프로젝트 디렉터리 또는 <code><nowiki>--project-directory</nowiki></code>가 가리키는 곳의 <code><nowiki>.env</nowiki></code>를 읽습니다. 자세한 동작은 Docker의 [https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/ 환경 변수 보간 안내]를 참고하세요. <code><nowiki>.env</nowiki></code>는 추적하지 않습니다. <code><nowiki>docker compose config</nowiki></code>는 비밀번호와 토큰 해시를 출력하므로 일반 확인에는 <code><nowiki>docker compose config --quiet</nowiki></code>를 사용합니다.
Compose는 현재 프로젝트 디렉터리 또는 <code><nowiki>--project-directory</nowiki></code>가 가리키는 곳의 <code><nowiki>.env</nowiki></code>를 읽습니다. 자세한 동작은 Docker의 [https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/ 환경 변수 보간 안내]를 참고하세요. <code><nowiki>.env</nowiki></code>는 추적하지 않습니다. <code><nowiki>docker compose config</nowiki></code>는 비밀번호와 토큰 해시를 출력하므로 일반 확인에는 <code><nowiki>docker compose config --quiet</nowiki></code>를 사용합니다.
124번째 줄: 126번째 줄:
| <code><nowiki>MW_ADMIN_USERNAME</nowiki></code> || 아니요 || <code><nowiki>S3Admin</nowiki></code>; 처음 만드는 관리자 계정 이름
| <code><nowiki>MW_ADMIN_USERNAME</nowiki></code> || 아니요 || <code><nowiki>S3Admin</nowiki></code>; 처음 만드는 관리자 계정 이름
|-
|-
| <code><nowiki>MW_ADMIN_PASSWORD</nowiki></code> || 예 || 처음 만드는 관리자 비밀번호
| <code><nowiki>MW_ADMIN_PASSWORD</nowiki></code> || 예 || 처음 만드는 관리자 비밀번호. Compose secret으로 one-shot <code><nowiki>mediawiki-install</nowiki></code>·<code><nowiki>bootstrap</nowiki></code>에만 마운트
|-
| <code><nowiki>S3RM_WIKI_USERNAME</nowiki></code> || 아니요 || <code><nowiki>S3ResearchAgent</nowiki></code>; MCP 전용 최소 권한 '''기본 계정'''. <code><nowiki>.env</nowiki></code>에는 <code><nowiki>@app-id</nowiki></code>를 붙이지 않음
|-
| <code><nowiki>S3RM_WIKI_PASSWORD</nowiki></code> || 예 || 기본 계정의 primary 비밀번호. one-shot <code><nowiki>bootstrap</nowiki></code>에서 계정 수렴에만 쓰고 장기 <code><nowiki>mcp-http</nowiki></code>에는 전달하지 않음
|-
|-
| <code><nowiki>S3RM_WIKI_USERNAME</nowiki></code> || 아니요 || <code><nowiki>S3ResearchAgent</nowiki></code>; MCP 전용 최소 권한 위키 계정
| <code><nowiki>S3RM_WIKI_BOT_APP_ID</nowiki></code> || 아니요 || <code><nowiki>mcp-http</nowiki></code>; API 전용 BotPassword app ID. 안전한 ASCII 1~32자
|-
|-
| <code><nowiki>S3RM_WIKI_PASSWORD</nowiki></code> || 예 || MCP 어댑터만 쓰는 위키 비밀번호
| <code><nowiki>S3RM_WIKI_BOT_PASSWORD</nowiki></code> || 예 || <code><nowiki>0-9</nowiki></code>, <code><nowiki>a-w</nowiki></code>로 된 정확히 32자 BotPassword. Compose secret으로 <code><nowiki>bootstrap</nowiki></code>과 <code><nowiki>mcp-http</nowiki></code>에만 마운트
|-
|-
| <code><nowiki>S3RM_WIKI_REVISION_HMAC_KEY</nowiki></code> || 예 || MCP와 MediaWiki 저장 훅만 공유하는 43~256자 URL-safe 서명 키
| <code><nowiki>S3RM_WIKI_REVISION_HMAC_KEY</nowiki></code> || 예 || MCP와 MediaWiki 저장 훅만 공유하는 43~256자 URL-safe 서명 키
|-
|-
| <code><nowiki>S3RM_AGENT_EDITS_PER_MINUTE</nowiki></code> || 아니요 || <code><nowiki>600</nowiki></code>; 공유 <code><nowiki>agent</nowiki></code> 계정의 편집 한도 91~6000회/분. 일반 계정과 편집 이외 작업에는 적용하지 않음
| <code><nowiki>S3RM_AGENT_EDITS_PER_MINUTE</nowiki></code> || 아니요 || <code><nowiki>6000</nowiki></code>; 공유 <code><nowiki>agent</nowiki></code> 계정의 편집 한도 91~6000회/분. 일반 계정과 편집 이외 작업에는 적용하지 않음
|-
| <code><nowiki>S3RM_JOBS_MAX_JOBS</nowiki></code> || 아니요 || <code><nowiki>25</nowiki></code>; job runner 한 batch의 최대 job 수, 1~100
|-
| <code><nowiki>S3RM_JOBS_MAX_SECONDS</nowiki></code> || 아니요 || <code><nowiki>20</nowiki></code>; job runner 한 batch의 wall-clock 상한, 5~120초
|-
| <code><nowiki>S3RM_JOBS_IDLE_SECONDS</nowiki></code> || 아니요 || <code><nowiki>5</nowiki></code>; 정상 batch 사이 대기, 1~60초
|-
| <code><nowiki>S3RM_JOBS_ERROR_BACKOFF_SECONDS</nowiki></code> || 아니요 || <code><nowiki>15</nowiki></code>; 실패 후 첫 재시도 대기, 5~300초
|-
| <code><nowiki>S3RM_JOBS_MAX_BACKOFF_SECONDS</nowiki></code> || 아니요 || <code><nowiki>300</nowiki></code>; 지수 backoff 상한, 최대 900초
|-
| <code><nowiki>S3RM_JOBS_HEALTH_STALE_SECONDS</nowiki></code> || 아니요 || <code><nowiki>120</nowiki></code>; 마지막 성공 시각이 이보다 오래되면 job runner를 unhealthy로 판정, 30~900초
|-
| <code><nowiki>S3RM_BINLOG_RETENTION_SECONDS</nowiki></code> || 아니요 || <code><nowiki>1209600</nowiki></code>; 로컬 MariaDB binary log 보존 14일. 원격 백업과 복원 시험 주기보다 길게 유지
|-
|-
| <code><nowiki>S3RM_HTTP_PORT</nowiki></code> || 아니요 || <code><nowiki>8080</nowiki></code>; MediaWiki 80번 포트에 연결한 호스트 진단 포트
| <code><nowiki>S3RM_HTTP_PORT</nowiki></code> || 아니요 || <code><nowiki>8080</nowiki></code>; MediaWiki 80번 포트에 연결한 호스트 진단 포트
165번째 줄: 185번째 줄:
|-
|-
| <code><nowiki>S3RM_MCP_ALLOW_INSECURE_HTTP</nowiki></code> || 아니요 || <code><nowiki>false</nowiki></code>; 클라이언트 주소는 HTTPS만 사용
| <code><nowiki>S3RM_MCP_ALLOW_INSECURE_HTTP</nowiki></code> || 아니요 || <code><nowiki>false</nowiki></code>; 클라이언트 주소는 HTTPS만 사용
|-
| <code><nowiki>S3RM_MCP_PROXY_SECRET</nowiki></code> || 예 || Caddy와 MCP만 공유하는 43~256자 URL-safe 값. 전달된 client IP 헤더를 인증하며 클라이언트에는 공개하지 않음
|-
| <code><nowiki>S3RM_MCP_WRITE_MAX_CONCURRENCY</nowiki></code> || 아니요 || <code><nowiki>8</nowiki></code>; 동시에 MediaWiki 쓰기로 진입하는 MCP 호출 수, 1~64
|-
| <code><nowiki>S3RM_MCP_WRITE_QUEUE_LIMIT</nowiki></code> || 아니요 || <code><nowiki>64</nowiki></code>; 동시 실행 밖에서 기다릴 쓰기 수, 0~1000
|-
| <code><nowiki>S3RM_MCP_WRITE_QUEUE_TIMEOUT_SECONDS</nowiki></code> || 아니요 || <code><nowiki>2</nowiki></code>; queue 대기 상한 0.05~30초. 넘으면 bounded busy 응답
|-
| <code><nowiki>S3RM_MCP_WRITE_RATE_PER_MINUTE</nowiki></code> || 아니요 || <code><nowiki>600</nowiki></code>; principal별 지속 쓰기 token 보충률, 1~6000회/분
|-
| <code><nowiki>S3RM_MCP_WRITE_RATE_BURST</nowiki></code> || 아니요 || <code><nowiki>120</nowiki></code>; principal별 즉시 burst token 상한, 1~1000
|-
| <code><nowiki>S3RM_MCP_WRITE_PRINCIPAL_LIMIT</nowiki></code> || 아니요 || <code><nowiki>10000</nowiki></code>; 메모리에 둘 최근 rate bucket 수, 100~100000. 초과 시 가장 오래된 bucket 제거
|-
|-
| <code><nowiki>S3RM_PUBLIC_HOST</nowiki></code> || 아니요 || <code><nowiki>s3wiki.yonsei.ac.kr</nowiki></code>; Caddy 주소
| <code><nowiki>S3RM_PUBLIC_HOST</nowiki></code> || 아니요 || <code><nowiki>s3wiki.yonsei.ac.kr</nowiki></code>; Caddy 주소
181번째 줄: 215번째 줄:
|}
|}


<code><nowiki>S3RM_AGENT_EDITS_PER_MINUTE</nowiki></code>를 바꾼 뒤에는 <code><nowiki>restart</nowiki></code>가 아니라 MediaWiki 컨테이너를 다시 만들어야 새 환경 변수가 들어갑니다.
<code><nowiki>S3RM_AGENT_EDITS_PER_MINUTE</nowiki></code>를 바꾼 뒤에는 <code><nowiki>restart</nowiki></code>가 아니라 MediaWiki 컨테이너를 다시 만들어야 새 환경 변수가 들어갑니다. 기존 MariaDB 컨테이너에 binary log 설정을 처음 적용한다면 재생성 전에 [[Help:S3 연구 메모리/백업과 복원#binary log를 처음 켜는 서버|최초 이전용 기준 백업]]을 먼저 만드세요. 일반 백업은 <code><nowiki>log_bin=ON</nowiki></code>을 강제합니다.


<pre><nowiki>
<pre><nowiki>
191번째 줄: 225번째 줄:
<pre><nowiki>
<pre><nowiki>
docker compose up -d --build --force-recreate mediawiki
docker compose up -d --build --force-recreate mediawiki
</nowiki></pre>
MediaWiki의 6000회/분은 모든 MCP 호출이 공유하는 <code><nowiki>agent</nowiki></code> 계정의 마지막 편집 상한입니다. 그 앞에서 <code><nowiki>mcp-http</nowiki></code>는 전체 동시 실행 8개와 대기 64개를 제한하고, principal별로 지속 600회/분·burst 120을 적용합니다. <code><nowiki>open</nowiki></code> 모드의 principal은 내부 비밀값까지 맞는 Caddy가 덮어쓴 client IP, <code><nowiki>github</nowiki></code>는 로그인 subject, <code><nowiki>token</nowiki></code>은 공유 token 하나입니다. 따라서 <code><nowiki>token</nowiki></code> 모드에서는 모든 쓰기 클라이언트가 600회/분 bucket도 공유합니다. 인증 모드를 바꾸지 않고 처리량만 조정할 때는 먼저 <code><nowiki>S3RM_MCP_WRITE_MAX_CONCURRENCY</nowiki></code>, queue 대기와 DB latency를 보고 필요한 값만 올립니다.
BotPassword 값은 MediaWiki가 로그인에서 허용하는 32자리 문자 집합 안에서 만듭니다. 출력한 값은 <code><nowiki>.env</nowiki></code>의 <code><nowiki>S3RM_WIKI_BOT_PASSWORD</nowiki></code>에 한 번만 옮기고 셸 기록, Git, 클라이언트 설정에는 넣지 않습니다.
<pre><nowiki>
openssl rand -hex 16
</nowiki></pre>
</nowiki></pre>


305번째 줄: 347번째 줄:
</nowiki></pre>
</nowiki></pre>


<code><nowiki>domain</nowiki></code> 프로필은 Caddy 컨테이너 하나를 시작합니다. Caddy는 TLS 인증서를 발급·갱신하고, HTTP를 HTTPS로 돌립니다. <code><nowiki>/mcp</nowiki></code>와 <code><nowiki>/mcp/*</nowiki></code>는 <code><nowiki>mcp-http</nowiki></code>로 보내고 나머지 경로는 MediaWiki로 보냅니다. MCP 경로를 다시 쓰거나 Lesson을 저장하지 않습니다. 동작 원리는 Caddy의 [https://caddyserver.com/docs/automatic-https 자동 HTTPS 안내]와 [https://caddyserver.com/docs/quick-starts/reverse-proxy 리버스 프록시 안내]를 참고하세요.
<code><nowiki>domain</nowiki></code> 프로필은 Caddy 컨테이너 하나를 시작합니다. Caddy는 TLS 인증서를 발급·갱신하고, HTTP를 HTTPS로 돌립니다. <code><nowiki>/mcp</nowiki></code>와 <code><nowiki>/mcp/*</nowiki></code>는 <code><nowiki>mcp-http</nowiki></code>로 보내고 나머지 경로는 MediaWiki로 보냅니다. 공개 요청의 client IP와 서버 전용 proxy secret 헤더를 항상 덮어쓰며, MCP는 secret이 일치할 때만 IP별 제한에 그 값을 사용합니다. 직접 진단 포트로 보낸 같은 이름의 헤더는 무시합니다. MCP 경로를 다시 쓰거나 Lesson을 저장하지 않습니다. 동작 원리는 Caddy의 [https://caddyserver.com/docs/automatic-https 자동 HTTPS 안내]와 [https://caddyserver.com/docs/quick-starts/reverse-proxy 리버스 프록시 안내]를 참고하세요.


정적 <code><nowiki>Authorization</nowiki></code> 헤더를 보낼 수 있는 클라이언트는 공용 Bearer 토큰으로 변경 도구 네 개를 사용할 수 있습니다. 호스팅형 ChatGPT와 Claude 커넥터는 같은 URL에서 공개 조회 도구를 사용할 수 있습니다. 이 제품에서 변경 도구를 쓰려면 OAuth 진입점이 추가로 필요합니다. OAuth를 붙이더라도 MCP나 위키를 하나 더 만들지 않습니다.
정적 <code><nowiki>Authorization</nowiki></code> 헤더를 보낼 수 있는 클라이언트는 공용 Bearer 토큰으로 변경 도구 네 개를 사용할 수 있습니다. 호스팅형 ChatGPT와 Claude 커넥터는 같은 URL에서 공개 조회 도구를 사용할 수 있습니다. 이 제품에서 변경 도구를 쓰려면 OAuth 진입점이 추가로 필요합니다. OAuth를 붙이더라도 MCP나 위키를 하나 더 만들지 않습니다.
327번째 줄: 369번째 줄:
서비스 역할은 다음과 같습니다.
서비스 역할은 다음과 같습니다.


* <code><nowiki>db</nowiki></code>: MariaDB와 <code><nowiki>db_data</nowiki></code> 볼륨
* <code><nowiki>db</nowiki></code>: MariaDB와 <code><nowiki>db_data</nowiki></code>, PITR용 <code><nowiki>db_binlogs</nowiki></code> 볼륨
* <code><nowiki>mediawiki-install</nowiki></code>: 새 볼륨에서만 관리자 secret을 읽어 CLI 설치를 하는 one-shot 작업. 기존 설정이 있으면 변경 없이 종료
* <code><nowiki>mediawiki</nowiki></code>: Apache/PHP, Semantic MediaWiki, Page Forms, 생성된 <code><nowiki>LocalSettings.php</nowiki></code>, <code><nowiki>mediawiki_config</nowiki></code>·<code><nowiki>mediawiki_images</nowiki></code> 볼륨
* <code><nowiki>mediawiki</nowiki></code>: Apache/PHP, Semantic MediaWiki, Page Forms, 생성된 <code><nowiki>LocalSettings.php</nowiki></code>, <code><nowiki>mediawiki_config</nowiki></code>·<code><nowiki>mediawiki_images</nowiki></code> 볼륨
* <code><nowiki>bootstrap</nowiki></code>: 스키마·양식 페이지를 맞추고 MCP 계정과 그룹을 만드는 일회성 작업
* <code><nowiki>bootstrap</nowiki></code>: 스키마·양식 페이지, MCP 기본 계정·<code><nowiki>agent</nowiki></code> 그룹과 API 전용 BotPassword를 맞추고 실제 BotPassword 로그인·유효 권한을 확인하는 일회성 작업
* <code><nowiki>mediawiki-jobs</nowiki></code>: <code><nowiki>runJobs</nowiki></code> 1 process를 한 번에 기본 25건·20초로 제한하고, 실패 시 최대 5분까지 지수 backoff하는 전용 runner
* <code><nowiki>embedding-model-init</nowiki></code>: 고정한 GGUF를 받아 크기와 SHA-256을 확인하는 일회성 작업
* <code><nowiki>embedding-model-init</nowiki></code>: 고정한 GGUF를 받아 크기와 SHA-256을 확인하는 일회성 작업
* <code><nowiki>embedding</nowiki></code>: 선택적 Vulkan 임베딩 런타임. 호스트 포트와 영구 벡터 저장소는 없음
* <code><nowiki>embedding</nowiki></code>: 선택적 Vulkan 임베딩 런타임. 호스트 포트와 영구 벡터 저장소는 없음
338번째 줄: 382번째 줄:
<pre><nowiki>
<pre><nowiki>
docker compose ps -a
docker compose ps -a
docker compose logs -f mediawiki bootstrap mcp-http caddy
docker compose logs -f mediawiki-install mediawiki bootstrap mediawiki-jobs mcp-http caddy
</nowiki></pre>
</nowiki></pre>


<code><nowiki>db</nowiki></code>, <code><nowiki>mediawiki</nowiki></code>, <code><nowiki>mcp-http</nowiki></code>, <code><nowiki>caddy</nowiki></code>가 실행 또는 healthy 상태이고, <code><nowiki>bootstrap</nowiki></code>이 코드 0으로 종료되면 정상입니다. Caddy 인증서도 발급되어야 합니다. <code><nowiki>bootstrap</nowiki></code>은 일회성 작업이므로 종료 상태가 정상입니다. <code><nowiki>docker compose up -d</nowiki></code>를 다시 실행해도 관리 스키마와 양식을 맞추며 일반 Lesson과 변경 이력은 보존합니다. 새 설치에는 Lesson 페이지가 없습니다. 첫 메모를 저장하기 전에는 최근 목록과 검색 결과가 비어 있어도 정상입니다.
<code><nowiki>db</nowiki></code>, <code><nowiki>mediawiki</nowiki></code>, <code><nowiki>mediawiki-jobs</nowiki></code>, <code><nowiki>mcp-http</nowiki></code>, <code><nowiki>caddy</nowiki></code>가 healthy 상태이고, <code><nowiki>mediawiki-install</nowiki></code><code><nowiki>bootstrap</nowiki></code>이 코드 0으로 종료되면 정상입니다. Caddy 인증서도 발급되어야 합니다. 두 one-shot 작업은 종료 상태가 정상입니다. <code><nowiki>docker compose up -d</nowiki></code>를 다시 실행해도 관리 스키마와 양식을 맞추며 일반 Lesson과 변경 이력은 보존합니다. 새 설치에는 Lesson 페이지가 없습니다. 첫 메모를 저장하기 전에는 최근 목록과 검색 결과가 비어 있어도 정상입니다.
 
&lt;!-- Contract marker: There is no browser installation step. --&gt; 브라우저 설치 단계는 없습니다. 저장된 <code><nowiki>LocalSettings.php</nowiki></code>가 없으면 <code><nowiki>mediawiki-install</nowiki></code> one-shot이 Compose secret의 관리자 비밀번호를 <code><nowiki>--passfile</nowiki></code>로 넘겨 MediaWiki CLI 설치를 완료합니다. 그 다음 <code><nowiki>mediawiki</nowiki></code> entrypoint가 DB 마이그레이션을 적용하고 Apache를 시작합니다. 장기 실행 <code><nowiki>mediawiki</nowiki></code> 컨테이너에는 <code><nowiki>MW_ADMIN_PASSWORD</nowiki></code>가 환경 변수나 secret으로 들어가지 않습니다. <code><nowiki>/mw-config/</nowiki></code>에 접속하거나, 브라우저 설치를 다시 시작하거나, 생성된 <code><nowiki>LocalSettings.php</nowiki></code>를 컨테이너에 복사하지 마세요. 설치 화면이 나오거나 세션 만료 오류가 보이면 [[Help:S3 연구 메모리/문제 해결#브라우저에 설치 세션 만료가 표시됨|문제 해결 안내]]를 따릅니다.


&lt;!-- Contract marker: There is no browser installation step. --&gt; 브라우저 설치 단계는 없습니다. 저장된 <code><nowiki>LocalSettings.php</nowiki></code>가 없으면 <code><nowiki>mediawiki</nowiki></code> entrypoint가 Apache를 시작하기 전에 MediaWiki CLI 설치를 실행하고 DB 마이그레이션을 적용합니다. <code><nowiki>/mw-config/</nowiki></code>에 접속하거나, 브라우저 설치를 다시 시작하거나, 생성된 <code><nowiki>LocalSettings.php</nowiki></code>를 컨테이너에 복사하지 마세요. 설치 화면이 나오거나 세션 만료 오류가 보이면 [[Help:S3 연구 메모리/문제 해결#브라우저에 설치 세션 만료가 표시됨|문제 해결 안내]]를 따릅니다.
장기 실행 <code><nowiki>mcp-http</nowiki></code>는 기본 계정의 <code><nowiki>S3RM_WIKI_PASSWORD</nowiki></code>를 받지 않습니다. <code><nowiki>bootstrap</nowiki></code>은 <code><nowiki>basic,createeditmovepage</nowiki></code> grant만 가진 BotPassword를 만들거나 같은 app ID의 비밀번호를 갱신하고, 로그인 이름 <code><nowiki>S3RM_WIKI_USERNAME@S3RM_WIKI_BOT_APP_ID</nowiki></code>로 실제 API 로그인을 확인합니다. <code><nowiki>mcp-http</nowiki></code>는 읽기 전용으로 마운트한 BotPassword secret을 시작할 때 읽습니다. BotPassword grant는 계정 권한을 추가하지 않으며, <code><nowiki>agent</nowiki></code> 그룹의 제한과 저장 훅 검증이 계속 적용됩니다.


[https://s3wiki.yonsei.ac.kr/]를 엽니다. 읽기와 검색은 로그인 없이 할 수 있습니다. 작성과 편집에는 이름이 있는 위키 계정이 필요합니다.
[https://s3wiki.yonsei.ac.kr/]를 엽니다. 읽기와 검색은 로그인 없이 할 수 있습니다. 작성과 편집에는 이름이 있는 위키 계정이 필요합니다.
388번째 줄: 434번째 줄:
</nowiki></pre>
</nowiki></pre>


서버에서 프로세스 상태를 먼저 확인합니다. <code><nowiki>S3RM_MCP_HTTP_PORT</nowiki></code>를 바꿨다면 8765도 같이 바꿉니다. 이 경로는 프로세스 상태만 반환하며 위키 데이터나 인증이 없습니다.
서버에서 프로세스 상태와 canonical store readiness를 차례로 확인합니다. <code><nowiki>S3RM_MCP_HTTP_PORT</nowiki></code>를 바꿨다면 8765도 같이 바꿉니다. <code><nowiki>/livez</nowiki></code>는 MCP 프로세스만, <code><nowiki>/readyz</nowiki></code>는 BotPassword 로그인과 필요한 위키 권한까지 확인합니다.


<pre><nowiki>
<pre><nowiki>
curl --fail --silent --show-error http://127.0.0.1:8765/healthz
curl --fail --silent --show-error http://127.0.0.1:8765/livez
curl --fail --silent --show-error http://127.0.0.1:8765/readyz
</nowiki></pre>
</nowiki></pre>


478번째 줄: 525번째 줄:
* 공용 MCP 토큰: <code><nowiki>./scripts/configure-mcp-token.sh --rotate</nowiki></code>를 실행하고 <code><nowiki>mcp-http</nowiki></code>를 다시 만듭니다. 변경 경로를 확인한 뒤 새 handoff를 전달하고 클라이언트를 재연결합니다. <code><nowiki>--rotate</nowiki></code>가 없으면 일치하는 기존 토큰을 보존합니다. 공개 조회는 계속되지만 이전·새 쓰기 토큰을 동시에 받을 수는 없습니다.
* 공용 MCP 토큰: <code><nowiki>./scripts/configure-mcp-token.sh --rotate</nowiki></code>를 실행하고 <code><nowiki>mcp-http</nowiki></code>를 다시 만듭니다. 변경 경로를 확인한 뒤 새 handoff를 전달하고 클라이언트를 재연결합니다. <code><nowiki>--rotate</nowiki></code>가 없으면 일치하는 기존 토큰을 보존합니다. 공개 조회는 계속되지만 이전·새 쓰기 토큰을 동시에 받을 수는 없습니다.
* revision HMAC 키: 새 URL-safe 키를 <code><nowiki>.env</nowiki></code>에 넣고 <code><nowiki>mediawiki</nowiki></code>와 <code><nowiki>mcp-http</nowiki></code>를 같은 작업 시간에 다시 만듭니다. 둘 중 하나만 이전 키를 쓰는 동안에는 revision을 실행하지 않습니다. 이 키는 어떤 클라이언트에도 전달하지 않습니다.
* revision HMAC 키: 새 URL-safe 키를 <code><nowiki>.env</nowiki></code>에 넣고 <code><nowiki>mediawiki</nowiki></code>와 <code><nowiki>mcp-http</nowiki></code>를 같은 작업 시간에 다시 만듭니다. 둘 중 하나만 이전 키를 쓰는 동안에는 revision을 실행하지 않습니다. 이 키는 어떤 클라이언트에도 전달하지 않습니다.
* MCP 위키 계정: <code><nowiki>S3RM_WIKI_PASSWORD</nowiki></code>를 바꾸고 <code><nowiki>bootstrap</nowiki></code>을 다시 실행한 뒤 <code><nowiki>mcp-http</nowiki></code>를 다시 만듭니다. <code><nowiki>bootstrap</nowiki></code>은 전용 계정과 <code><nowiki>agent</nowiki></code> 그룹을 맞추고 높은 권한을 제거합니다.
* MCP 기본 계정: <code><nowiki>S3RM_WIKI_PASSWORD</nowiki></code>는 one-shot 계정 수렴에만 씁니다. 값을 바꾸면 <code><nowiki>bootstrap</nowiki></code>을 다시 실행하되, 장기 <code><nowiki>mcp-http</nowiki></code>에는 이 값을 추가하지 마세요.
* MCP BotPassword: 새 <code><nowiki>openssl rand -hex 16</nowiki></code> 값을 <code><nowiki>S3RM_WIKI_BOT_PASSWORD</nowiki></code>에 넣고 아래 순서로 전환합니다. <code><nowiki>bootstrap</nowiki></code>은 같은 app ID를 갱신하고 <code><nowiki>basic,createeditmovepage</nowiki></code> grant와 로그인·권한을 확인합니다. ```bash docker compose stop mcp-http docker compose run --rm bootstrap docker compose up -d --build --force-recreate mcp-http curl --fail --silent --show-error http://127.0.0.1:8765/healthz \ | jq --exit-status '.canonical_store == "ok"' ``` 새 자격 증명이 확인되기 전에는 이전 <code><nowiki>.env</nowiki></code>의 안전한 복구 사본을 지우지 마세요. app ID를 바꾸면 이전 app ID의 BotPassword는 자동으로 삭제되지 않습니다. 새 경로를 확인한 뒤 관리 화면 <code><nowiki>Special:BotPasswords</nowiki></code>에서 이전 app ID 하나만 폐기합니다.
* 일반 위키 계정: MediaWiki 화면에서 비밀번호를 바꿉니다. DB 테이블을 직접 편집하지 마세요.
* 일반 위키 계정: MediaWiki 화면에서 비밀번호를 바꿉니다. DB 테이블을 직접 편집하지 마세요.
* MariaDB 계정: MariaDB 공식 계정 관리 절차로 서버 계정을 바꾼 뒤, 같은 작업 시간에 <code><nowiki>.env</nowiki></code>를 갱신합니다.
* MariaDB 계정: MariaDB 공식 계정 관리 절차로 서버 계정을 바꾼 뒤, 같은 작업 시간에 <code><nowiki>.env</nowiki></code>를 갱신합니다.

2026년 7월 18일 (토) 17:36 판

이 페이지는 저장소 문서에서 자동으로 동기화됩니다. 위키에서 직접 편집하지 마세요.

한 대의 서버에서 위키와 공용 MCP를 함께 실행합니다. 브라우저는 [1]에 접속하고, 모든 에이전트는 [2]에 연결합니다. 두 주소는 하나의 Semantic MediaWiki를 사용합니다. 클라이언트 PC에는 이 저장소, Docker 권한, MediaWiki 에이전트 비밀번호가 필요하지 않습니다.

데이터베이스, 생성된 위키 설정, 업로드 파일은 Docker 볼륨에 저장합니다. 저장소에는 데이터, 자격 증명, 생성된 LocalSettings.php를 넣지 않습니다.

준비 사항

  • Docker Engine 24 이상과 Docker Compose v2 플러그인 또는 최신 Docker Desktop
  • Python 3.11 이상과 uv
  • 메모리 4 GiB 이상, 디스크 여유 공간 8 GiB 이상
  • Bash, make, openssl
  • 백업·복원용 GNU/Linux 호스트: Bash 4 이상, GNU coreutils(stat, sha256sum), GNU tar, gzip, 일반 파일 권한 지원
  • s3wiki.yonsei.ac.kr이 서버의 165.132.118.220으로 연결된 DNS
  • 서버까지 들어오는 TCP 80, 443

macOS와 Windows의 Docker Desktop은 화면 확인에 사용할 수 있습니다. 백업·복원 스크립트는 이 Compose 프로젝트를 실행하는 Linux VM이나 Linux Docker 호스트에서 실행하세요. uv가 없다면 공식 설치 안내를 따릅니다.

필수 명령을 확인합니다.

docker version
docker compose version
make --version
bash --version
sha256sum --version
uv --version

docker version이 클라이언트 버전만 출력하지 않고 Docker 데몬에도 연결되어야 합니다. 이 확인 전에 도메인 설정 스크립트를 실행하지 마세요. 첫 빌드에서는 이미지와 고정된 PHP·Python 의존성을 내려받으므로 인터넷 연결이 필요합니다.

일반 운영 경로는 위 HTTPS 도메인 하나입니다. 읽기와 검색은 공개됩니다. S3RM_MCP_WRITE_AUTH_MODE=token이면 생성·관계·근거 추가와 기존 Lesson의 조건부 수정에 같은 공용 Bearer 토큰이 필요합니다. open은 네 변경 도구를 인증 없이 허용합니다. github는 GitHub 로그인을 거쳐 MCP용 access·refresh token을 발급하며 이 모드에서는 MCP 연결 전체가 OAuth 인증 대상입니다. Caddy는 TLS를 종료하고 /mcp와 OAuth 경로를 MCP 서비스로 보냅니다. 나머지 경로는 MediaWiki로 보냅니다.

기본값은 token입니다. 짧은 공개 시험에서만 .env에 다음 값을 넣고 mcp-http를 다시 만드세요.

S3RM_MCP_WRITE_AUTH_MODE=open

다시 잠그려면 값을 token으로 바꿉니다. 기존 S3RM_MCP_TOKEN_SHA256은 지우지 않아도 되며 open 모드에서는 쓰기에 사용하지 않습니다. 사용자 연결은 Linux, macOS, Windows 문서에 있고 제품별 제약은 mcp-clients.md에 있습니다.

GitHub OAuth 모드 준비

GitHub의 Settings → Developer settings → OAuth Apps에서 앱을 만들고 callback URL을 정확히 다음 값으로 등록합니다.

https://s3wiki.yonsei.ac.kr/oauth/github/callback

그다음 .env를 설정합니다.

S3RM_MCP_WRITE_AUTH_MODE=github
S3RM_GITHUB_CLIENT_ID=발급받은-client-id
S3RM_GITHUB_CLIENT_SECRET=발급받은-client-secret
S3RM_GITHUB_ALLOWED_USERS=허용할-github-사용자
S3RM_GITHUB_ALLOWED_ORGS=
S3RM_GITHUB_ALLOW_ANY_USER=false

사용자 또는 조직 allowlist 중 하나는 설정해야 합니다. 정말 모든 GitHub 사용자를 허용할 때만 S3RM_GITHUB_ALLOW_ANY_USER=true를 사용하세요. GitHub 모드는 저장소 권한을 요청하지 않습니다. 조직 allowlist를 사용할 때만 조직 확인을 위해 read:org scope를 요청합니다. mcp:write scope는 생성, 관계, 근거와 조건부 수정에 함께 적용됩니다.

현재 OAuth client와 token 상태는 mcp-http 프로세스 메모리에만 있습니다. 서비스를 다시 만들면 기존 OAuth 연결이 무효가 되므로 ChatGPT 앱을 다시 인증하거나 다시 만들어야 합니다.

환경 설정

저장소 루트에서 실행합니다.

cp .env.example .env
chmod 600 .env

비밀번호의 change-me 값을 모두 바꿉니다. S3RM_MCP_TOKEN_SHA256=change-me-...는 다음 단계의 토큰 스크립트가 바꾸므로 그대로 둡니다. 비밀번호마다 새 값을 만드세요.

openssl rand -base64 36

공용 MCP 설정

.env의 비밀번호를 바꾼 뒤 등록된 도메인을 설정합니다.

./scripts/configure-public-domain.sh s3wiki.yonsei.ac.kr \
  --expected-ip 165.132.118.220

스크립트는 DNS를 확인하고 위키 URL, MCP URL, loopback 바인딩, HTTPS 설정, Compose domain 프로필을 맞춥니다. 공용 쓰기 토큰은 화면에 출력하지 않습니다. 서버의 mode 0600 .env에는 SHA-256 해시만 저장하고, 원문 토큰과 S3RM_MCP_URL은 mode 0600 ~/.config/s3-research-memory/client.env에 저장합니다. 두 파일의 값이 이미 일치하면 토큰을 바꾸지 않습니다.

모든 클라이언트에 같은 MCP URL을 설정합니다. 조회만 할 때는 토큰이 필요하지 않습니다. 메모를 생성·수정하거나 관계·근거를 추가할 에이전트에만 공용 토큰을 전달합니다. 이 토큰으로 페이지 삭제나 위키 관리를 할 수는 없습니다. 위키 관리자·DB 비밀번호를 토큰으로 재사용하지 마세요.

서버에서 쓰기 연결을 시험할 때는 값을 출력하지 않고 불러옵니다.

set -a
source "$HOME/.config/s3-research-memory/client.env"
set +a

원문 토큰은 필요한 사람에게 비공개 채널로 전달합니다. Git, 공개 이슈, 프롬프트, 스크린샷에는 넣지 마세요. 공용 토큰을 바꾸면 쓰기 클라이언트를 함께 갱신해야 합니다. 공개 조회에는 영향이 없습니다.

설정 스크립트는 비어 있거나 예시인 S3RM_WIKI_BOT_PASSWORD에 32자리 API 전용 비밀번호를 만들고, 비어 있는 S3RM_WIKI_REVISION_HMAC_KEY에 URL-safe 무작위 32바이트 값을 만듭니다. 이후 실행과 토큰 교체에서는 둘 다 보존합니다. 스크립트를 쓰지 않는 설정은 openssl rand -hex 32로 직접 만들 수 있습니다. 이 키는 mcp-http와 MediaWiki 저장 훅만 공유하며 클라이언트에는 전달하지 않습니다. 공용 토큰이나 위키 비밀번호와 같은 값을 쓰지 마세요. 키가 없거나 형식이 맞지 않으면 MCP가 시작을 거부합니다.

같은 스크립트는 별도의 S3RM_MCP_PROXY_SECRET도 생성합니다. 이 값은 Caddy와 mcp-http만 공유하며, Caddy가 덮어쓴 client IP 헤더임을 확인하는 데만 씁니다. revision HMAC 키와 재사용하거나 클라이언트에 전달하지 않습니다.

Compose는 현재 프로젝트 디렉터리 또는 --project-directory가 가리키는 곳의 .env를 읽습니다. 자세한 동작은 Docker의 환경 변수 보간 안내를 참고하세요. .env는 추적하지 않습니다. docker compose config는 비밀번호와 토큰 해시를 출력하므로 일반 확인에는 docker compose config --quiet를 사용합니다.

환경 변수

변수 필수 기본값·용도
MARIADB_DATABASE 아니요 s3_research_memory; 위키 데이터베이스 이름
MARIADB_USER 아니요 s3wiki; MediaWiki DB 계정
MARIADB_PASSWORD 제한된 MediaWiki DB 계정 비밀번호
MARIADB_ROOT_PASSWORD 초기화·복구 때만 쓰는 MariaDB 관리자 비밀번호
MW_SITE_NAME 아니요 S3 연구 메모리; 브라우저에 표시할 이름
MW_SERVER_URL 아니요 https://s3wiki.yonsei.ac.kr; 끝에 /가 없는 외부 주소
MW_ADMIN_USERNAME 아니요 S3Admin; 처음 만드는 관리자 계정 이름
MW_ADMIN_PASSWORD 처음 만드는 관리자 비밀번호. Compose secret으로 one-shot mediawiki-install·bootstrap에만 마운트
S3RM_WIKI_USERNAME 아니요 S3ResearchAgent; MCP 전용 최소 권한 기본 계정. .env에는 @app-id를 붙이지 않음
S3RM_WIKI_PASSWORD 기본 계정의 primary 비밀번호. one-shot bootstrap에서 계정 수렴에만 쓰고 장기 mcp-http에는 전달하지 않음
S3RM_WIKI_BOT_APP_ID 아니요 mcp-http; API 전용 BotPassword app ID. 안전한 ASCII 1~32자
S3RM_WIKI_BOT_PASSWORD 0-9, a-w로 된 정확히 32자 BotPassword. Compose secret으로 bootstrapmcp-http에만 마운트
S3RM_WIKI_REVISION_HMAC_KEY MCP와 MediaWiki 저장 훅만 공유하는 43~256자 URL-safe 서명 키
S3RM_AGENT_EDITS_PER_MINUTE 아니요 6000; 공유 agent 계정의 편집 한도 91~6000회/분. 일반 계정과 편집 이외 작업에는 적용하지 않음
S3RM_JOBS_MAX_JOBS 아니요 25; job runner 한 batch의 최대 job 수, 1~100
S3RM_JOBS_MAX_SECONDS 아니요 20; job runner 한 batch의 wall-clock 상한, 5~120초
S3RM_JOBS_IDLE_SECONDS 아니요 5; 정상 batch 사이 대기, 1~60초
S3RM_JOBS_ERROR_BACKOFF_SECONDS 아니요 15; 실패 후 첫 재시도 대기, 5~300초
S3RM_JOBS_MAX_BACKOFF_SECONDS 아니요 300; 지수 backoff 상한, 최대 900초
S3RM_JOBS_HEALTH_STALE_SECONDS 아니요 120; 마지막 성공 시각이 이보다 오래되면 job runner를 unhealthy로 판정, 30~900초
S3RM_BINLOG_RETENTION_SECONDS 아니요 1209600; 로컬 MariaDB binary log 보존 14일. 원격 백업과 복원 시험 주기보다 길게 유지
S3RM_HTTP_PORT 아니요 8080; MediaWiki 80번 포트에 연결한 호스트 진단 포트
S3RM_WIKI_BIND_ADDRESS 아니요 127.0.0.1; 진단 포트를 외부에 열지 않음
S3RM_WIKI_DIRECT_URL 아니요 비워 두면 직접 주소를 사용하지 않음. 외부 HTTP 화면을 명시적으로 열 때만 http://SERVER_IP:8080처럼 전체 주소를 입력하고 bind를 0.0.0.0으로 설정
S3RM_MCP_PUBLIC_URL 클라이언트 주소 https://s3wiki.yonsei.ac.kr/mcp
S3RM_MCP_WRITE_AUTH_MODE 아니요 token; open, token, github 중 하나
S3RM_MCP_TOKEN_SHA256 token 모드 쓰기 토큰의 64자리 16진수 SHA-256 해시. 원문은 저장하지 않음
S3RM_GITHUB_CLIENT_ID github 모드 GitHub OAuth App의 client ID
S3RM_GITHUB_CLIENT_SECRET github 모드 GitHub OAuth App의 client secret. 커밋하지 않음
S3RM_GITHUB_ALLOWED_USERS 선택 허용할 GitHub 사용자명. 쉼표로 구분
S3RM_GITHUB_ALLOWED_ORGS 선택 허용할 GitHub 조직명. 쉼표로 구분
S3RM_GITHUB_ALLOW_ANY_USER 아니요 false; true면 모든 GitHub 계정을 허용
S3RM_MCP_BIND_ADDRESS 아니요 도메인 프로필에서는 127.0.0.1. 공개 진입점은 Caddy
S3RM_MCP_HTTP_PORT 아니요 8765; MCP 컨테이너 8000번 포트에 연결한 loopback 진단 포트
S3RM_MCP_ALLOWED_HOSTS 아니요 쉼표로 나눈 추가 Host 값. 공개 URL의 host는 자동 허용
S3RM_MCP_ALLOWED_ORIGINS 아니요 쉼표로 나눈 추가 HTTP(S) Origin. 공개 URL의 origin은 자동 허용
S3RM_MCP_ALLOW_INSECURE_HTTP 아니요 false; 클라이언트 주소는 HTTPS만 사용
S3RM_MCP_PROXY_SECRET Caddy와 MCP만 공유하는 43~256자 URL-safe 값. 전달된 client IP 헤더를 인증하며 클라이언트에는 공개하지 않음
S3RM_MCP_WRITE_MAX_CONCURRENCY 아니요 8; 동시에 MediaWiki 쓰기로 진입하는 MCP 호출 수, 1~64
S3RM_MCP_WRITE_QUEUE_LIMIT 아니요 64; 동시 실행 밖에서 기다릴 쓰기 수, 0~1000
S3RM_MCP_WRITE_QUEUE_TIMEOUT_SECONDS 아니요 2; queue 대기 상한 0.05~30초. 넘으면 bounded busy 응답
S3RM_MCP_WRITE_RATE_PER_MINUTE 아니요 600; principal별 지속 쓰기 token 보충률, 1~6000회/분
S3RM_MCP_WRITE_RATE_BURST 아니요 120; principal별 즉시 burst token 상한, 1~1000
S3RM_MCP_WRITE_PRINCIPAL_LIMIT 아니요 10000; 메모리에 둘 최근 rate bucket 수, 100~100000. 초과 시 가장 오래된 bucket 제거
S3RM_PUBLIC_HOST 아니요 s3wiki.yonsei.ac.kr; Caddy 주소
S3RM_PUBLIC_HTTP_PORT 아니요 80; 리디렉션과 인증서 발급용 공개 HTTP 포트
S3RM_PUBLIC_HTTPS_PORT 아니요 443; 공개 HTTPS 포트
COMPOSE_PROFILES 아니요 domain은 Caddy, embedding은 내부 GPU 임베딩 서비스를 실행. 쉼표로 함께 지정 가능
S3RM_MAX_OUTPUT_BYTES 아니요 32768; 도구 결과 제한 2048~65536바이트. 전체 JSON-RPC 응답은 별도 212992바이트 제한
S3RM_SEARCH_SCAN_LIMIT 아니요 200; 후보 스캔 제한 20~1000페이지
S3RM_WIKI_TIMEOUT_SECONDS 아니요 20; MediaWiki 요청 제한 1~120초

S3RM_AGENT_EDITS_PER_MINUTE를 바꾼 뒤에는 restart가 아니라 MediaWiki 컨테이너를 다시 만들어야 새 환경 변수가 들어갑니다. 기존 MariaDB 컨테이너에 binary log 설정을 처음 적용한다면 재생성 전에 최초 이전용 기준 백업을 먼저 만드세요. 일반 백업은 log_bin=ON을 강제합니다.

docker compose up -d --force-recreate mediawiki

저장소의 LocalSettings.s3.php도 함께 바뀐 배포에서는 이미지를 먼저 다시 만듭니다.

docker compose up -d --build --force-recreate mediawiki

MediaWiki의 6000회/분은 모든 MCP 호출이 공유하는 agent 계정의 마지막 편집 상한입니다. 그 앞에서 mcp-http는 전체 동시 실행 8개와 대기 64개를 제한하고, principal별로 지속 600회/분·burst 120을 적용합니다. open 모드의 principal은 내부 비밀값까지 맞는 Caddy가 덮어쓴 client IP, github는 로그인 subject, token은 공유 token 하나입니다. 따라서 token 모드에서는 모든 쓰기 클라이언트가 600회/분 bucket도 공유합니다. 인증 모드를 바꾸지 않고 처리량만 조정할 때는 먼저 S3RM_MCP_WRITE_MAX_CONCURRENCY, queue 대기와 DB latency를 보고 필요한 값만 올립니다.

BotPassword 값은 MediaWiki가 로그인에서 허용하는 32자리 문자 집합 안에서 만듭니다. 출력한 값은 .envS3RM_WIKI_BOT_PASSWORD에 한 번만 옮기고 셸 기록, Git, 클라이언트 설정에는 넣지 않습니다.

openssl rand -hex 16

Compose의 mcp-http는 내부 http://mediawiki/api.php만 사용합니다. 이 주소와 위키 비밀번호는 클라이언트에 전달하지 않습니다.

선택적 의미 검색

기본 S3RM_EMBEDDING_PROVIDER=disabled는 모델을 실행하지 않고 기존 전체 텍스트와 관계 검색을 사용합니다. 이 저장소에는 이 서버의 AMD Vega GPU에 맞춘 비공개 llama.cpp Vulkan 서비스가 포함되어 있습니다. 모델은 Qwen/Qwen3-Embedding-0.6B F16이며, 결과는 1024차원입니다. 공식 Qwen GGUFllama.cpp 서버를 고정된 revision과 이미지 digest로 사용합니다.

다음 두 값만 바꾸면 .env.example의 고정 모델 설정을 그대로 사용할 수 있습니다.

S3RM_EMBEDDING_PROVIDER=openai-compatible
COMPOSE_PROFILES=domain,embedding

고정 모델을 확인하고 서비스를 시작합니다. 첫 실행은 약 1.2GB 모델을 받습니다.

make embedding-up

embedding-model-init만 인터넷에 연결됩니다. 이 작업은 모델 저장소 commit, 파일 크기와 SHA-256을 모두 확인한 뒤 embedding_models 볼륨에 원자적으로 게시합니다. 실제 embedding 서비스는 호스트 포트를 열지 않으며 mcp-http와만 공유하는 내부 embedding-backend 네트워크를 사용합니다. Vega의 render node 하나만 컨테이너에 전달합니다.

실행 중인 모델, 차원과 MCP 연결 설정을 다시 확인할 수 있습니다.

make verify-embedding

s3rm-qwen3-retrieval-v1 profile은 모델 artifact digest, 1024차원, last pooling, 16K token 입력 계약과 세 facet의 query instruction을 한 generation으로 묶습니다. document에는 instruction을 붙이지 않습니다. 희귀 CJK의 byte fallback도 16K를 넘지 않도록 각 document facet은 보수적으로 4,000자에서 자릅니다. 이 값 중 하나를 바꾸면 새 exact index를 처음부터 만듭니다. 서로 다른 generation의 벡터는 섞지 않습니다.

변수 필수 기본값·범위
S3RM_EMBEDDING_PROVIDER 아니요 disabled; disabled, openai-compatible 중 하나
S3RM_EMBEDDING_PROFILE 사용 시 번들 모델은 s3rm-qwen3-retrieval-v1; 기존 제공자는 legacy-v1
S3RM_EMBEDDING_API_URL 임베딩 사용 시 자격 증명·query·fragment가 없는 HTTP(S) embeddings endpoint
S3RM_EMBEDDING_API_KEY 제공자에 따라 요청에만 쓰는 secret. 커밋하지 않음
S3RM_EMBEDDING_MODEL 임베딩 사용 시 제공자의 고정 모델 식별자
S3RM_EMBEDDING_MODEL_REVISION profile에 따라 generation에 넣는 immutable revision. 번들 모델은 sha256:<GGUF digest>
S3RM_EMBEDDING_DIMENSIONS 임베딩 사용 시 벡터 차원 1~4096
S3RM_EMBEDDING_MODEL_SOURCE_REVISION 번들 모델 downloader가 사용하는 40자리 Hugging Face commit
S3RM_EMBEDDING_MODEL_SHA256 번들 모델 내려받은 GGUF의 64자리 SHA-256
S3RM_EMBEDDING_MODEL_SIZE_BYTES 번들 모델 내려받은 GGUF의 정확한 바이트 수
S3RM_EMBEDDING_DRI_DEVICE 번들 모델 이 서버의 외장 Vega render node /dev/dri/renderD128
S3RM_EMBEDDING_GPU_DEVICE 번들 모델 컨테이너에서 보이는 장치 Vulkan0
S3RM_RENDER_GID 번들 모델 render node의 group ID. 이 서버는 987
S3RM_EMBEDDING_TIMEOUT_SECONDS 아니요 번들 모델은 60; 허용 범위 0.5~120초
S3RM_EMBEDDING_BATCH_SIZE 아니요 번들 모델은 6; 요청 한 번에 1~128개
S3RM_EMBEDDING_MAX_CONCURRENCY 아니요 번들 모델은 1; 동시 요청 1~16개
S3RM_EMBEDDING_QUEUE_LIMIT 아니요 32; 대기 요청 1~1000개
S3RM_EMBEDDING_QUEUE_TIMEOUT_SECONDS 아니요 1; 대기 제한 0.05~30초
S3RM_EMBEDDING_ALLOW_INSECURE_HTTP 아니요 번들 내부 HTTP는 true; 외부 endpoint는 HTTPS 사용
S3RM_EMBEDDING_INSECURE_HTTP_HOSTS 내부 HTTP 사용 시 허용할 정확한 Host 목록. 번들 서비스는 embedding:8080
S3RM_SEMANTIC_CANDIDATE_LIMIT 아니요 50; facet별 후보 5~100개
S3RM_SEMANTIC_REFRESH_SECONDS 아니요 300; 주기 대조와 실패 후 재시도 간격 10~86400초. dirty hint는 즉시 갱신

의미 인덱스는 mcp-http 메모리의 파생 후보 인덱스입니다. Semantic MediaWiki가 계속 기준 저장소이며, 제공자나 인덱스가 실패하면 제한된 기존 검색으로 대체합니다. S3RM_SEARCH_SCAN_LIMIT × 3 × S3RM_EMBEDDING_DIMENSIONS는 3,000,000개 벡터 구성요소를 넘을 수 없습니다. 이 조합 검사는 exact index의 메모리 사용을 제한합니다. 외부 OpenAI 호환 API를 대신 사용할 수도 있습니다. 이 경우 legacy-v1 또는 별도로 검증한 profile을 사용하고 HTTPS endpoint를 설정합니다. Lesson 의미 검색 필드와 아직 저장하지 않은 query가 외부로 전송되므로 보존·학습 사용·region·비용 조건을 확인한 뒤 켜세요. 로컬 서비스 장애가 외부 API로 자동 전환되지는 않습니다.

설정만으로 기본 검색 순서는 바뀌지 않습니다. 클라이언트가 search_lessons 또는 find_analogiesretrieval_mode=hybrid_v1을 명시해야 합니다. mcp-http는 시작할 때 단 하나의 백그라운드 작업으로 전체 Lesson exact index를 프리웜합니다. hybrid 요청은 이 작업을 기다리지 않습니다. 아직 snapshot이 없으면 effective_mode=legacy, semantic_status=building으로 즉시 대체하고, 구축에 실패해 사용할 snapshot도 없으면 unavailable을 표시합니다.

인덱스가 준비된 뒤에는 Lesson 저장 dirty hint가 즉시 같은 단일 백그라운드 갱신을 시작합니다. S3RM_SEMANTIC_REFRESH_SECONDS가 지나면 다음 hybrid 요청이 브라우저 편집까지 포함한 전체 대조를 시작합니다. 요청은 기존 snapshot을 계속 사용하며 갱신 중에는 stale, 갱신 실패 뒤에는 degraded가 될 수 있습니다. 확인할 때는 작은 요청을 다시 보내 effective_mode=hybrid_v1, semantic_status=ready인지 봅니다.

search_lessons(query="단편화", retrieval_mode="hybrid_v1", limit=1)

직접 HTTP 화면을 함께 열어야 한다면 다음 두 값을 같이 설정합니다. MediaWiki는 정확히 이 Host로 들어온 요청에만 직접 주소를 사용합니다. 임의의 Host 헤더를 신뢰하지 않으며 정식 주소와 MCP 주소는 HTTPS 도메인으로 유지합니다.

S3RM_WIKI_BIND_ADDRESS=0.0.0.0
S3RM_WIKI_DIRECT_URL=http://165.132.118.220:8080

이 설정은 Docker의 호스트 리스너만 엽니다. 학교 경계 보안정책에서 TCP 8080이 차단되어 있으면 외부 접속은 계속 timeout입니다. 직접 주소는 위키 화면에만 쓰며 MCP URL로 등록하지 않습니다.

진단 포트만 바꿀 때는 외부 브라우저 주소를 그대로 둡니다.

S3RM_HTTP_PORT=8090
MW_SERVER_URL=https://s3wiki.yonsei.ac.kr

도메인과 Caddy 프로필

도메인 스크립트는 여러 번 실행해도 됩니다. Docker를 시작하지 않으며, 기존 토큰과 해시가 일치하면 토큰을 바꾸지 않습니다.

./scripts/configure-public-domain.sh s3wiki.yonsei.ac.kr \
  --expected-ip 165.132.118.220

domain 프로필은 Caddy 컨테이너 하나를 시작합니다. Caddy는 TLS 인증서를 발급·갱신하고, HTTP를 HTTPS로 돌립니다. /mcp/mcp/*mcp-http로 보내고 나머지 경로는 MediaWiki로 보냅니다. 공개 요청의 client IP와 서버 전용 proxy secret 헤더를 항상 덮어쓰며, MCP는 secret이 일치할 때만 IP별 제한에 그 값을 사용합니다. 직접 진단 포트로 보낸 같은 이름의 헤더는 무시합니다. MCP 경로를 다시 쓰거나 Lesson을 저장하지 않습니다. 동작 원리는 Caddy의 자동 HTTPS 안내리버스 프록시 안내를 참고하세요.

정적 Authorization 헤더를 보낼 수 있는 클라이언트는 공용 Bearer 토큰으로 변경 도구 네 개를 사용할 수 있습니다. 호스팅형 ChatGPT와 Claude 커넥터는 같은 URL에서 공개 조회 도구를 사용할 수 있습니다. 이 제품에서 변경 도구를 쓰려면 OAuth 진입점이 추가로 필요합니다. OAuth를 붙이더라도 MCP나 위키를 하나 더 만들지 않습니다.

시작

필수 변수와 Compose 모델을 출력 없이 확인합니다.

docker compose config --quiet

이미지를 빌드하고 서비스를 시작합니다. 아래 명령은 HTTPS 위키 API와 /mcp도 확인합니다.

make domain-up

이 확인은 명령을 실행한 서버 기준입니다. 학교 밖에서 80/443이 열렸는지는 다른 네트워크의 브라우저에서도 확인해야 합니다. 이미 실행 중인 서버의 HTTPS와 MCP만 다시 확인할 때는 make verify-domain을 사용합니다.

서비스 역할은 다음과 같습니다.

  • db: MariaDB와 db_data, PITR용 db_binlogs 볼륨
  • mediawiki-install: 새 볼륨에서만 관리자 secret을 읽어 CLI 설치를 하는 one-shot 작업. 기존 설정이 있으면 변경 없이 종료
  • mediawiki: Apache/PHP, Semantic MediaWiki, Page Forms, 생성된 LocalSettings.php, mediawiki_config·mediawiki_images 볼륨
  • bootstrap: 스키마·양식 페이지, MCP 기본 계정·agent 그룹과 API 전용 BotPassword를 맞추고 실제 BotPassword 로그인·유효 권한을 확인하는 일회성 작업
  • mediawiki-jobs: runJobs 1 process를 한 번에 기본 25건·20초로 제한하고, 실패 시 최대 5분까지 지수 backoff하는 전용 runner
  • embedding-model-init: 고정한 GGUF를 받아 크기와 SHA-256을 확인하는 일회성 작업
  • embedding: 선택적 Vulkan 임베딩 런타임. 호스트 포트와 영구 벡터 저장소는 없음
  • mcp-http: 공개 조회와 토큰 기반 변경을 제공하는 공용 Streamable HTTP 어댑터. Lesson DB나 영구 볼륨은 없음
  • caddy: 위키와 MCP의 공개 HTTPS 진입점

첫 시작 상태를 확인합니다.

docker compose ps -a
docker compose logs -f mediawiki-install mediawiki bootstrap mediawiki-jobs mcp-http caddy

db, mediawiki, mediawiki-jobs, mcp-http, caddy가 healthy 상태이고, mediawiki-installbootstrap이 코드 0으로 종료되면 정상입니다. Caddy 인증서도 발급되어야 합니다. 두 one-shot 작업은 종료 상태가 정상입니다. docker compose up -d를 다시 실행해도 관리 스키마와 양식을 맞추며 일반 Lesson과 변경 이력은 보존합니다. 새 설치에는 Lesson 페이지가 없습니다. 첫 메모를 저장하기 전에는 최근 목록과 검색 결과가 비어 있어도 정상입니다.

<!-- Contract marker: There is no browser installation step. --> 브라우저 설치 단계는 없습니다. 저장된 LocalSettings.php가 없으면 mediawiki-install one-shot이 Compose secret의 관리자 비밀번호를 --passfile로 넘겨 MediaWiki CLI 설치를 완료합니다. 그 다음 mediawiki entrypoint가 DB 마이그레이션을 적용하고 Apache를 시작합니다. 장기 실행 mediawiki 컨테이너에는 MW_ADMIN_PASSWORD가 환경 변수나 secret으로 들어가지 않습니다. /mw-config/에 접속하거나, 브라우저 설치를 다시 시작하거나, 생성된 LocalSettings.php를 컨테이너에 복사하지 마세요. 설치 화면이 나오거나 세션 만료 오류가 보이면 문제 해결 안내를 따릅니다.

장기 실행 mcp-http는 기본 계정의 S3RM_WIKI_PASSWORD를 받지 않습니다. bootstrapbasic,createeditmovepage grant만 가진 BotPassword를 만들거나 같은 app ID의 비밀번호를 갱신하고, 로그인 이름 S3RM_WIKI_USERNAME@S3RM_WIKI_BOT_APP_ID로 실제 API 로그인을 확인합니다. mcp-http는 읽기 전용으로 마운트한 BotPassword secret을 시작할 때 읽습니다. BotPassword grant는 계정 권한을 추가하지 않으며, agent 그룹의 제한과 저장 훅 검증이 계속 적용됩니다.

[3]를 엽니다. 읽기와 검색은 로그인 없이 할 수 있습니다. 작성과 편집에는 이름이 있는 위키 계정이 필요합니다.

  • /index.php/Special:Version: Semantic MediaWiki, Page Forms, S3 Research Memory 확장 기능 확인
  • /index.php/연구_메모리#create-a-lesson: 문서 ID를 정하고 Lesson: 메모 작성
  • /index.php/Special:Search: MediaWiki 전문 검색
  • /index.php/Special:AllPages?namespace=3000: 모든 Lesson: 페이지

브라우저 계정 만들기

자기 가입은 꺼져 있습니다. 설정에서 익명 그룹(*)의 createaccount 권한을 제거했고, 관리자 그룹(sysop)만 계정을 만들 수 있습니다. 새 설치에서는 다음 순서로 일반 편집 계정을 만드세요.

  1. MW_ADMIN_USERNAMEMW_ADMIN_PASSWORD로 로그인합니다.
  2. /index.php/Special:CreateAccount를 엽니다.
  3. 사용할 사람의 이름을 알 수 있는 사용자 이름과 새 임시 비밀번호를 입력합니다.
  4. 계정을 만든 뒤 /index.php/Special:ListUsers에서 확인합니다.
  5. sysop, bureaucrat, agent 그룹은 추가하지 않습니다. 로그인한 일반 계정은 자동으로 user 권한을 받아 양식으로 메모를 작성·편집할 수 있습니다.
  6. 새 계정으로 로그인해 비밀번호를 바꾸고 메모 양식이 열리는지 확인합니다.

관리자 계정이나 S3RM_WIKI_USERNAME 계정을 일상 편집에 함께 쓰지 마세요. 계정과 권한 기준은 security.md에 정리되어 있습니다.

설치 확인

로컬 테스트와 Compose 검사를 실행합니다.

make test
make check-compose

make test는 Compose 없이 메모리 내 위키 어댑터로 스키마, 에이전트 권한, 관계, 페이지네이션, 직렬화 출력 제한을 확인합니다. make check-compose는 테스트용 필수 값을 넣은 뒤 Compose 배포 모델을 확인합니다.

브라우저에서는 다음을 확인합니다.

  1. 로그아웃 상태에서 Special:Version, 검색, Lesson 페이지가 열리고, 작성·편집은 로그인을 요구하는지 확인합니다.
  2. 새 설치라면 Lesson 네임스페이스의 Special:AllPages가 비어 있어도 정상입니다.
  3. 로그인한 일반 계정으로 /index.php/연구_메모리#create-a-lesson을 열고 작성 양식이 보이는지 확인합니다. 확인용 가짜 메모는 저장하지 않습니다.
  4. 첫 실제 메모를 저장할 때 검색에 바로 나오는지, 필드와 근거 인용이 보이는지, 역사 보기에 변경 이력이 생겼는지 확인합니다.

클라이언트를 연결하기 전 공용 MCP 확인

.envS3RM_MCP_PUBLIC_URL과 같은 주소를 설정합니다.

export S3RM_MCP_URL='https://s3wiki.yonsei.ac.kr/mcp'

서버에서 프로세스 상태와 canonical store readiness를 차례로 확인합니다. S3RM_MCP_HTTP_PORT를 바꿨다면 8765도 같이 바꿉니다. /livez는 MCP 프로세스만, /readyz는 BotPassword 로그인과 필요한 위키 권한까지 확인합니다.

curl --fail --silent --show-error http://127.0.0.1:8765/livez
curl --fail --silent --show-error http://127.0.0.1:8765/readyz

이어서 토큰 없이 MCP 초기화를 보냅니다. HTTP나 JSON 오류가 있으면 0이 아닌 코드로 끝납니다.

python3 - <<'PY'
import json
import os
import urllib.request

payload = json.dumps({
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
        "protocolVersion": "2025-11-25",
        "capabilities": {},
        "clientInfo": {"name": "s3rm-setup-smoke", "version": "1"},
    },
}).encode()
request = urllib.request.Request(
    os.environ["S3RM_MCP_URL"],
    data=payload,
    headers={
        "Accept": "application/json, text/event-stream",
        "Content-Type": "application/json",
    },
    method="POST",
)
with urllib.request.urlopen(request, timeout=20) as response:
    result = json.load(response)
assert result.get("result", {}).get("serverInfo", {}).get("name") == "S3 Research Memory"
print("anonymous MCP initialize: ok")
PY

토큰이 없는 클라이언트도 초기화, 도구 목록, search_lessons, get_lesson, audit_lessons, find_related_lessons, find_analogies를 사용할 수 있습니다. mcp-clients.md에 따라 연결하고 search_lessons(limit=1)을 실행합니다. 다른 PC의 클라이언트에서도 같은 결과가 나오는지 확인하세요.

변경 도구가 필요한 클라이언트에는 플랫폼 설정으로 공용 토큰을 불러옵니다. 이 토큰으로 생성, 관계, 근거와 조건부 수정을 모두 호출합니다. 연결 확인만 하려고 가짜 메모를 만들지 않습니다. 쓰기 권한 경계는 make test로 확인하고, 실제 첫 메모를 남길 때 MediaWiki 이력, audit_lessons와 검색 결과를 확인합니다.

평소 시작과 종료

설치가 끝난 서비스를 시작합니다.

docker compose up -d
docker compose ps -a
make verify-domain

마지막 명령은 서버에서 본 HTTPS 위키와 MCP를 확인합니다. 다른 PC에서 접속되는지는 그 PC의 브라우저나 MCP 클라이언트로 따로 확인합니다.

볼륨을 남기고 종료합니다.

docker compose down

MediaWiki만 다시 시작합니다.

docker compose restart mediawiki

공용 MCP 프로세스만 다시 시작합니다.

docker compose restart mcp-http
docker compose ps mcp-http

의도적으로 설정을 바꾼 뒤 스키마·계정을 다시 맞춥니다.

docker compose up -d --force-recreate bootstrap
docker compose logs bootstrap

일반 운영 중에는 docker compose down --volumes, docker volume rm, 수동 SQL 삭제를 실행하지 마세요. 위키의 기준 데이터가 사라집니다.

자격 증명 변경

.env 값만 바꿔도 MariaDB나 MediaWiki에 이미 저장된 자격 증명은 바뀌지 않습니다. 해당 제품의 계정 관리 절차로 먼저 바꾸고, 같은 작업 시간에 .env와 관련 서비스를 함께 갱신합니다.

  • 공용 MCP 토큰: ./scripts/configure-mcp-token.sh --rotate를 실행하고 mcp-http를 다시 만듭니다. 변경 경로를 확인한 뒤 새 handoff를 전달하고 클라이언트를 재연결합니다. --rotate가 없으면 일치하는 기존 토큰을 보존합니다. 공개 조회는 계속되지만 이전·새 쓰기 토큰을 동시에 받을 수는 없습니다.
  • revision HMAC 키: 새 URL-safe 키를 .env에 넣고 mediawikimcp-http를 같은 작업 시간에 다시 만듭니다. 둘 중 하나만 이전 키를 쓰는 동안에는 revision을 실행하지 않습니다. 이 키는 어떤 클라이언트에도 전달하지 않습니다.
  • MCP 기본 계정: S3RM_WIKI_PASSWORD는 one-shot 계정 수렴에만 씁니다. 값을 바꾸면 bootstrap을 다시 실행하되, 장기 mcp-http에는 이 값을 추가하지 마세요.
  • MCP BotPassword: 새 openssl rand -hex 16 값을 S3RM_WIKI_BOT_PASSWORD에 넣고 아래 순서로 전환합니다. bootstrap은 같은 app ID를 갱신하고 basic,createeditmovepage grant와 로그인·권한을 확인합니다. ```bash docker compose stop mcp-http docker compose run --rm bootstrap docker compose up -d --build --force-recreate mcp-http curl --fail --silent --show-error http://127.0.0.1:8765/healthz \ | jq --exit-status '.canonical_store == "ok"' ``` 새 자격 증명이 확인되기 전에는 이전 .env의 안전한 복구 사본을 지우지 마세요. app ID를 바꾸면 이전 app ID의 BotPassword는 자동으로 삭제되지 않습니다. 새 경로를 확인한 뒤 관리 화면 Special:BotPasswords에서 이전 app ID 하나만 폐기합니다.
  • 일반 위키 계정: MediaWiki 화면에서 비밀번호를 바꿉니다. DB 테이블을 직접 편집하지 마세요.
  • MariaDB 계정: MariaDB 공식 계정 관리 절차로 서버 계정을 바꾼 뒤, 같은 작업 시간에 .env를 갱신합니다.

먼저 백업하고, 변경 뒤 조회를 확인합니다. 공용 토큰 절차는 operations.md에 있습니다.

업데이트

MediaWiki 기본 이미지는 정확한 버전으로 고정되어 있습니다. PHP 의존성은 docker/mediawiki/composer.lock, Python 의존성은 mcp/uv.lock을 사용합니다. MariaDB 이미지는 호환 태그를 사용합니다.

  1. MediaWiki, Semantic MediaWiki, Page Forms, MariaDB, MCP 의존성의 릴리스 안내와 호환표를 확인합니다.
  2. operations.md에 따라 전체 백업을 만들고 확인합니다.
  3. 버전 고정을 추적 가능한 커밋에서 바꿉니다. 실제 서버에서 검증하지 않은 floating 버전을 쓰지 마세요.
  4. make testmake check-compose를 실행합니다.
  5. 별도 볼륨에 빌드하고 복원 시험을 합니다.
  6. 확인 뒤 실제 서버 이미지를 다시 만들고 bootstrap의 마이그레이션·동기화를 완료합니다.
docker compose build --pull mediawiki mcp-http
docker compose up -d
docker compose logs mediawiki bootstrap mcp-http caddy

새 MediaWiki에서 만든 DB를 더 오래된 MediaWiki로 복원하지 마세요. 호환성은 Semantic MediaWiki 설치 안내, Page Forms 설치 안내, MediaWiki 업그레이드 안내를 기준으로 확인합니다.