팀 모드와 그룹
기본적으로 aipager는 개인 봇이에요. 나와 봇만 있는 개인 채팅 하나이고 다른 사람은 없어요. 팀 모드는 더 많은 사람에게 열어 줘요. 여러 개발자가 함께 세션을 다루는 Telegram 그룹, 다른 사람과 봇의 개인 채팅, 또는 둘 다예요. 채팅마다 자기 멤버가 있고, 멤버마다 거기서 무엇을 할 수 있는지 정하는 역할이 있어요. 메시지 보내기, Claude의 질문과 권한 요청에 답하기, 세션 관리, 또는 보기만 하기예요.
기본적으로 aipager는 개인 봇이에요. 나와 봇만 있는 개인 채팅 하나이고 다른 사람은 없어요. 팀 모드는 더 많은 사람에게 열어 줘요. 여러 개발자가 함께 세션을 다루는 Telegram 그룹, 다른 사람과 봇의 개인 채팅, 또는 둘 다예요. 채팅마다 자기 멤버가 있고, 멤버마다 거기서 무엇을 할 수 있는지 정하는 역할이 있어요. 메시지 보내기, Claude의 질문과 권한 요청에 답하기, 세션 관리, 또는 보기만 하기예요.
팀 모드는 선택 사항이에요. 내 개인 채팅만 있는 설치는 예전과 똑같이 동작해요.
신중하게 정하세요#
Telegram 사용자를 채팅에 추가하면 그 사람이 aipager가 실행되는 컴퓨터에서 코드를 실행할 수 있게 돼요. 역할에 따라 그 사람은:
- Claude가 shell 명령, 파일 수정, 네트워크 연결로 바꾸는 메시지를 보낼 수 있어요.
- 권한 요청에 답할 수 있어요(허용 / 거부). Claude가 어떤 도구에 대해 물을지는 여전히 내
~/.claude/settings.json이 정하지만, 승인할 수 있는 역할을 가진 사람은 누구나 허용을 누를 수 있어요. - 세션을 시작하고, 멈추고, 종료하고, 세션 사이를 오갈 수 있어요.
멤버 목록은 이 컴퓨터의 SSH 접근 권한처럼 다루세요. 감사 로그(~/.claude/aipager-audit.jsonl)는 누가 무엇을 했는지 기록하지만, 일이 일어난 뒤에야 기록해요.
설정#
모든 것은 aipager config로 설정해요. 파일을 직접 편집할 일은 없어요.
처음 실행할 때(아직 설정이 없을 때). 설정 마법사가 네 단계를 물어요:
- 봇의 token;
- 봇과의 내 개인 채팅(자동 감지, 또는 붙여 넣기);
- 내 계정이
owner인지(기본값은 예. 아니요라고 하면admin이 되고, 이 역할은 나에게도 안전 기준선을 유지해요). - 의존성을 확인하고 Claude Code hook을 설치해요.
내 개인 채팅을 첫 번째 채팅으로 써요. 기본 모드는 묻지 않아요. 요약에 "New sessions start in Auto mode; owners and admins can choose Full access in Telegram under /settings → New sessions."(새 세션은 자동 모드로 시작하고, 소유자와 관리자는 Telegram의 /settings → 새 세션에서 전체 권한 모드를 고를 수 있어요)라고 나와요. 그다음 "Connected. Add a team group or other people now?"를 묻고, 선택지는 Add a group, Add a person (DM), I'm done이에요.
그 뒤의 실행은 편집 메뉴를 열어요. 메뉴는 지금 있는 채팅을 보여 주고 다음을 제공해요:
- Add a group scope: 새 그룹과 그 멤버(아래 참고).
- Add a DM scope: 다른 사람과 봇의 개인 채팅. 개인 채팅에는 멤버가 한 명이에요. 그 사람의 역할을 고르세요(기본값
user.owner는 친구 자신의 컨테이너처럼 한 사람만 쓰는 설치에서만 고르세요).can_manage가 없는 역할(예:user)이라면 이어서 그 사람의 루트 폴더를 묻고, 기본값(~/aipager-users/<label>)을 미리 채워 둬요. 이미 개인 채팅이 설정된 사람이라면 설정 마법사가 그렇다고 알려 주고 아무것도 바꾸지 않아요. 역할을 바꾸려면 Edit a member를 쓰세요. - Edit a scope: 그룹에는 Add a member가, 모든 채팅에는 Rename, Edit scope deny_tools, Set the members' root folder(루트 폴더 참고), Remove this scope가 있어요(마지막 채팅은 지울 수 없어요). 채팅을 지우기 전에 그 채팅의 세션을 종료하세요. 실행 중인 세션은 계속 그 채팅에 메시지를 올리고, 이제 어떤 채팅도 Telegram에서 그 세션을 다룰 수 없어요(컴퓨터에서는
aipager session ls와aipager session kill <name>으로 여전히 할 수 있어요). - Edit a member: Set role, Edit member deny_tools, Remove member(개인 채팅에는 없고, 그룹의 마지막 멤버에게는 적용되지 않아요). 소유자나 관리자(또는
bypass_role_denies가 있는 내 역할)라면 Edit member deny_tools는 금지된 도구가 그 역할에 적용되지 않는다고만 알려 줘요. - Test bot reachability, View policy, Re-install Claude Code hooks, Refresh bot token.
그룹 추가하기#
- 봇을 그룹에 추가한 다음 Add a group scope를 고르세요(처음 실행할 때는 Add a group).
- 그룹의 ID. 자동 감지를 고른 다음 그룹에서
/start를 보내거나, ID(음수)를 붙여 넣으세요. 설정 마법사가 테스트 메시지를 보내고 도착했는지 물어요. 이미 설정된 그룹이라면 여기서 "This group is already set up. Use Edit a scope → Add a member."와 함께 멈추고 아무것도 바꾸지 않아요. - 그룹의 라벨(설정 마법사와 Mini App에 나와요).
- 먼저 나. 내가 내 개인 채팅의 소유자라면 설정 마법사가 나를 그룹의 첫 멤버로,
owner역할로 추가하자고 제안해요(기본값은 예, 건너뛸 수 있어요). 그룹에 소유자나 관리자가 없으면 거기서는 아무도 전체 권한 모드,/settings,/update를 쓸 수 없어요. - 다른 멤버, 한 명씩: 찾는 방법(자동 감지, 또는 ID나
@handle붙여 넣기), 라벨(채팅에서@label로 나와요), 역할(기본값user). 역할 목록에는owner가 경고와 함께 있어요. 소유자는 aipager 설정, 봇 token, Claude 로그인 정보까지 포함해 이 컴퓨터를 완전히 다룰 수 있어요. 이 역할은 나에게만 주세요. 그룹에서 전체 권한 모드,/settings,/update를 써야 하는 사람에게는admin을 주세요. (봇과의 자기 개인 채팅에서는 메시지를 보내고 Claude에게 답할 수 있는 사람이라면 역할과 상관없이 자기 세션에 전체 권한 모드를 쓸 수 있어요.) - 선택 사항인 규칙: "Block file edits (Write, Edit) for
usermembers in this group? Admins and the owner are not affected."(이 그룹의user멤버에게 파일 수정(Write, Edit)을 막을까요? 관리자와 소유자는 영향을 받지 않아요.) 기본값은 아니요예요.user는 원래 세션의 프로젝트 폴더 안에만 쓸 수 있어요. 예를 고르면 역할이 역할 규칙을 건너뛸 수 없는 멤버(user,read_only, 그리고bypass_role_denies없이 내가 만든 역할)의 작업에서 모든 Write와 Edit가 묻지 않고 거부되고, 작업이 멈춰요. 그러면 그 부분은 관리자나 소유자가 보내야 해요. - 멤버의 루트 폴더,
~/aipager-users/group-<label>로 미리 채워져요. 그룹의user와read_only멤버가 여기서 세션을 시작하고 일해요(루트 폴더 참고).
이미 설정된 그룹에 누군가를 추가하려면 Edit a scope → 그 그룹 → Add a member를 쓰세요. 그룹에 이미 있는 사람은 모두 자리와 역할이 그대로예요.
ID 찾기 (자동 감지)#
멤버를 자동 감지하면 아직 추가되지 않은 사람의 가장 최근 메시지를 가져와서, 쓰기 전에 "Is this the person to add?"(추가할 사람이 이 사람인가요?)라고 물어요(아니요라고 하면 그 사람을 건너뛰고 계속 찾아요). 그 사람의 Telegram 사용자 이름을 라벨로 제안해요. 그룹에서는 그 사람이 봇을 불러야 해요. 봇을 멘션하거나 /start를 보내세요(봇이 읽는 것 참고). 설정 마법사는 어디서 보고 있는지 알려 줘요:
- "Watching through the running daemon"(실행 중인 aipager를 통해):
aipager start가 실행 중인 동안 aipager가 봇에게 오는 모든 메시지를 받으니, 설정 마법사는 aipager가~/.claude/aipager-pending-users.json에 기록한 것을 읽어요. aipager는 아직 서비스하지 않는 그룹에서 봇을 부른 사람(그룹의 ID, 제목, 종류, 그리고 그 사람의 ID, 사용자 이름, 이름)과, 서비스하는 그룹에서 멤버가 아닌 사람을 기록해요. 서비스하지 않는 그룹에서는 절대 답하지 않아요. 아직 설정되지 않은 사람의 개인 메시지에는 답하고 기록하지 않으니, aipager가 실행 중일 때 누군가의 개인 채팅을 추가하려면 그 사람의 ID를 붙여 넣으세요(또는 먼저 aipager를 멈추세요). - "Watching Telegram directly"(Telegram에서 직접): aipager가 멈춰 있을 때는 설정 마법사가 직접 Telegram에 봇의 최근 메시지를 물어봐요.
처음 실행할 때 내 개인 채팅은 항상 Telegram에서 읽어요. 익명 그룹 관리자의 메시지, 또는 그룹이나 채널 이름으로 올린 메시지는 세지 않아요. 보낸 사람 ID(1087968824, 136817688, 777000)가 그렇게 올리는 모든 사람에게 같아서, 입력했든 감지했든 설정 마법사가 거부해요. 그 사람에게 자기 이름으로 올려 달라고 하세요.
설정은 어디에 있나요#
설정 마법사는 ~/.config/aipager/aipager.yaml(권한 0600)을 써요. 봇의 token, 봇이 서비스하는 채팅, 채팅마다의 멤버와 그 역할이에요. 역할마다 무엇을 할 수 있는지는 ~/.config/aipager/policy.yaml에 있고, 이 파일은 내 것이에요. 설정 마법사는 절대 쓰지 않아요(규칙이 작동하는 방식 참고). 예전 team.yaml 형식으로 설정한 설치는 자동으로 바뀌어요. aipager가 시작할 때 하거나, 다음 실행 때 설정 마법사가 해요.
재시작 없이 다시 불러오기#
편집 메뉴에서 채팅이나 멤버를 바꿀 때마다(그룹이나 개인 채팅 추가 또는 삭제, 멤버 추가 또는 삭제, 역할 설정, deny_tools 편집, 채팅 이름 바꾸기), 그리고 처음 실행이 끝날 때 그룹이나 사람을 추가했거나 하다 만 그룹을 이어서 마쳤을 때(거기서 추가한 모든 것에 대해 한 번 다시 불러와요), aipager config는 실행 중인 aipager에 다시 불러오기 지시(SIGUSR1)를 보내고 "Scopes reloaded live (no daemon restart needed)"를 보여 줘요. 다시 불러오기는 aipager.yaml과 policy.yaml을 다시 읽어요. 디스크의 설정이 거부될 것이라면 설정 마법사가 "Not applied"와 이유를 보여 주고, 내가 고칠 때까지 aipager는 예전 설정을 유지해요. 설정 마법사가 어느 프로세스가 aipager인지 확신하지 못하면 aipager를 재시작하라고 해요.
다시 불러오기가 바로 적용하는 것:
- 채팅마다 누가 어떤 역할과 규칙으로 봇을 쓸 수 있는지;
- 새 그룹이나 개인 채팅이 메시지를 받기 시작하고 자기
/메뉴를 받아요. 지운 채팅은 멈춰요(그 세션은 내가 종료할 때까지 계속 실행돼요). - 그 채팅에서 더 이상 메시지를 보낼 수 없는 사람의 보류 중인 메시지(요청에 답하기를 기다리거나 다른 사람의 작업이 끝나기를 기다리는 것)는 버려져요. 🤷를 받고 봇은 “Claude에게 보내지 않았어요: @bob 님은 이제 여기서 메시지를 보낼 수 없어요.”라고 답해요.
- 지워졌거나 권한을 잃은 사람의 실행 중인 작업은 남은 부분 동안 예전 규칙과 새 규칙 가운데 더 엄격한 것만 유지해요(지워진 사람이라면 제한된 최소 권한). 터미널에서 입력한 작업은 Telegram 메시지가 합쳐지지 않았다면 바뀌지 않아요. aipager는 그것이 누구의 것인지 기록하지 않으니, 그 채팅의 소유자 누구든(또는 개인 채팅이라면 그 멤버가) 권한을 잃으면 그런 작업이 좁혀져요. aipager가 재시작할 때 이미 실행 중이던 작업(아직 작업 중이거나 질문이나 권한 요청을 기다리는 중)은 그 채팅에서 권한을 잃은 모든 멤버에 맞춰 좁혀져요. 권한이 올라가도 실행 중인 작업이 넓어지는 일은 절대 없어요. 새 권한은 그 사람의 다음 작업부터 적용돼요.
재시작이 필요한 것은 봇 token뿐이에요(Refresh bot token 뒤에 aipager config가 그렇게 알려 줘요). 실행 중인 aipager가 없으면 다시 불러오기 지시를 보내지 않아요. 다음 aipager start가 새 설정을 읽어요.
직접 다시 불러오려면(예: policy.yaml을 편집한 뒤) aipager 프로세스에만 신호를 보내세요. 백그라운드 서비스라면:
pid=$(systemctl --user show -p MainPID --value aipager.service)
[ "${pid:-0}" -gt 0 ] && kill -USR1 "$pid"
(이 확인이 중요해요. 서비스가 실행 중이 아니면 MainPID가 0이고, kill -USR1 0은 내 shell 자신의 프로세스들에 신호를 보내요.)
aipager start로 직접 시작한 aipager라면 그 프로세스에 kill -USR1 <pid>를 보내세요. pgrep -f 'aipager start'의 결과 모두에 신호를 보내지 마세요(aipager를 실행하는 shell이나 컨테이너도 걸리고, SIGUSR1은 그것을 처리하지 않는 프로세스를 멈춰요). 그리고 --kill-whom=main 없이 systemctl --user kill을 쓰지 마세요(서비스의 세션은 서비스와 프로세스 그룹을 같이 써서 멈춰 버려요).
다시 불러올 때 aipager.yaml이나 policy.yaml이 망가졌거나 aipager.yaml이 없으면, aipager는 로그에 경고를 남기고 메모리에 이전 설정을 유지해요. 오타 하나나 옮겨진 파일 하나가 나를 밖에 가두거나 다른 사람을 들여보낼 수는 없어요. 다시 불러오기는 이미 채팅이 설정된 aipager를 절대 개인 모드로 되돌리지 않아요(그건 재시작만 해요).
봇의 관리자 권한과 개인정보 모드#
봇을 그룹의 관리자로 만들고 메시지 고정 권한을 주세요. 봇이 그룹의 상태 표시줄을 고정하려면 이 권한이 필요해요(모두에게 보이는 것 참고). 이 권한이 없어도 그룹은 동작하지만 상태 표시줄이 없어요. 나중에 이 권한을 주었다면 상태 표시줄이 나타나도록 aipager를 재시작하세요.
그룹 관리자인 봇은 BotFather의 개인정보 모드와 상관없이 그룹의 모든 메시지를 받아요. 그래도 안전해요. aipager는 봇이 읽는 것에 나온 메시지만 처리하고 나머지는 무시하니까, 그룹의 잡담이 세션에 닿는 일은 절대 없어요. 관리자가 아닌 봇은 기본적으로 개인정보 모드이고(켜 둔 채로 두세요), 그때 Telegram은 그룹 메시지 가운데 일부만 봇에게 넘겨줘요. 내 메시지가 닿지 않으면 봇의 메시지에 답장하거나 명령을 쓰세요. 개인정보 설정은 aipager가 처리하는 것을 바꾸지 않아요.
Telegram에서 그룹의 관리자라고 aipager에서 권한이 더 생기지는 않아요. 누가 무엇을 할 수 있는지는 aipager.yaml에 있는 그 사람의 역할로만 정해져요.
Telegram이 그룹을 업그레이드할 때#
그룹을 공개로 바꾸거나, 주제를 켜거나, 멤버가 200명을 넘거나, 일부 관리자 권한을 바꾸면 Telegram이 일반 그룹을 supergroup으로 바꾸고, 업그레이드하면 그룹에 새 채팅 ID가 생겨요. aipager는 저절로 따라가요. 업그레이드를 보면(그룹 안의 Telegram 알림, 또는 예전 ID로 처음 올릴 때) aipager.yaml에서 그룹의 항목(scope)을 새 ID로 옮기고, 그룹의 세션, 메시지 대상, /settings를 함께 옮기고, 그룹에 한 줄을 올려요: “Telegram이 이 그룹을 업그레이드했어요. aipager도 함께 옮겨 왔으니 따로 할 일은 없어요.” 고정된 상태 표시줄은 업그레이드된 그룹에서 새로 시작해요. aipager의 로그에는 두 ID를 적은 경고가 있어요.
aipager 내부의 세션 이름은 예전 ID를 유지하고(나는 절대 보지 않아요), aipager.yaml은 이 이동에 대한 짧은 chat_migrations 기록을 남겨서, 업그레이드 전에 시작한 세션은 재시작한 뒤에도 업그레이드된 그룹에 속해요. 예전 ID가 붙은 세션 내부 이름(/name__g<old id>)은 업그레이드된 그룹에서 쓸 수 있고 다른 곳에서는 쓸 수 없어요. 업그레이드 때 aipager가 멈춰 있었다면 다음에 시작할 때 알아채요. 업그레이드 순간 실행 중이던 작업은 끝까지 실행되고 답변도 업그레이드된 그룹에 닿지만, 작업 카드는 그 작업의 남은 시간 동안 다시 나오지 않아요. 새 ID가 이미 별도의 항목으로 설정되어 있다면 아무것도 옮기지 않고 로그가 그렇다고 알려 줘요. aipager config로 둘 중 하나를 지우세요. aipager config의 “Test bot reachability”는 업그레이드된 그룹을 만나면 새 ID를 보여 줘요.
시작할 때마다 aipager는 그룹마다 한 번 조회해요. Telegram이 그룹을 찾을 수 없다거나 봇이 그 그룹을 쓸 수 없다고 답하면(그룹이 지워졌거나, 봇이 내보내졌거나, aipager가 멈춘 동안 그룹이 업그레이드됐는데 Telegram이 어느 새 ID인지 알려 주지 않은 경우), aipager의 로그에 그 항목의 이름을 적은 경고가 남아요. 봇이 아직 그룹에 있는지 확인하고, 그룹이 업그레이드됐다면 aipager config에서 Add a group scope로 그룹을 다시 추가하고(그룹은 새 ID를 받아요. 멤버도 다시 추가하세요), Edit a scope → Remove this scope로 예전 항목을 지우세요(예전 ID는 더 이상 쓸 수 없어요). aipager는 아무것도 올리지 않고, 계속 다시 시도하지도 않아요.
포럼 주제는 지원하지 않아요#
팀마다 일반 그룹(주제 없음)을 하나씩 쓰세요. 주제를 켠 그룹에서도 aipager는 동작하지만, 한 주제 안에 머물지 않아요:
- 명령이나 메시지에 대한 답(예:
/status, 또는 “어느 세션인가요?”에 대한 답)은 그것을 보낸 주제에 남아요. - 알림, 작업 카드, 답변, 권한 요청, 고정된 상태 표시줄은 주제 없이 그룹에 올라가서 General에 떨어질 수 있어요.
- 사람마다 그룹 전체에서 현재 세션이 하나이고, 주제마다 하나가 아니에요.
주제를 켜면 일반 그룹도 supergroup으로 업그레이드되고, aipager는 그것을 따라가요(위 참고).
역할#
기본 역할이 네 개 있어요(aipager/safety.py 참고). 사람의 역할은 지금 다루고 있는 채팅에서의 역할이에요. Telegram이나 Mini App에서는 아무도 멤버나 역할을 바꿀 수 없어요. 컴퓨터의 aipager config만 할 수 있고, 그래서 이 일은 소유자의 손에 남아요.
owner | admin | user | read_only | |
|---|---|---|---|---|
메시지 보내기(can_prompt) | ✅ | ✅ | ✅ | ❌ |
Claude에게 답하기: 허용, 거부, 질문(can_approve) | ✅ | ✅ | ✅ | ❌ |
관리: 전체 권한 모드, 그룹의 /settings, /update, 음성 메시지 기능 설치(can_manage) | ✅ | ✅ | ❌ (전체 권한 모드: 자기 개인 채팅에서 ✅) | ❌ |
| Telegram이나 Mini App에서 멤버와 역할 바꾸기 | ❌ | ❌ | ❌ | ❌ |
역할의 금지 규칙 건너뛰기(bypass_role_denies) | ✅ | ✅ | ❌ | ❌ |
안전 기준선 건너뛰기(bypass_safety) | ✅ | ❌ | ❌ | ❌ |
| Bash와 다른 코드 실행 도구 | ✅ | ✅ (규칙은 최선의 노력일 뿐) | ❌ | ❌ |
| 모든 슬래시 명령 | ✅ | ✅ | 명령어 키보드의 명령, /compact, 모델 변경만 | user와 같음 |
세션을 시작하는 곳(/new, 재개, 재시작, 모드 전환) | 어디든 | 어디든 | 채팅의 루트 폴더 안에서만 | user와 같음 |
| 파일을 쓰는 곳 | 어디든 | 안전 기준선이 허용하는 어디든 | 세션의 프로젝트 폴더(루트 폴더 안에 있을 때)와 세션의 scratchpad, 홈 폴더는 절대 아님 | user와 같음 |
| 내 홈 폴더에서 읽기 | 안전 기준선이 허용하는 어디든 | 안전 기준선이 허용하는 어디든 | 루트 폴더 안에서만(컴퓨터의 다른 폴더는 예전처럼, 다른 멤버의 루트 폴더는 절대 안 됨) | user와 같음 |
로그인 정보가 든 파일(~/.ssh, ~/.git-credentials, ...) | 읽을 수 있음 | 읽을 수 있음 | 절대 안 됨(기본값) | 절대 안 됨(기본값) |
Grep/Glob | 어디든 | aipager의 보호된 폴더를 뺀 어디든(그런 폴더이거나, 그 안에 있거나, 그것을 품은 폴더에서는 검색하지 않음) | 세션의 프로젝트 폴더와 그 scratchpad | user와 같음 |
-
owner: 기본 안전 기준선까지 포함한 완전한 권한. 딱 한 사람이어야 해요. 이 컴퓨터를 다루는 사람이에요.
-
admin: 소유자가 Telegram과 Mini App에서 할 수 있는 모든 것. 세션을 전체 권한 모드로 바꾸기(그리고 이 모드로 세션 시작하기), 전체 권한 모드로 세션 재개하기, 그룹의
/settings바꾸기,/update실행, 음성 메시지 기능 설치, 모든 슬래시 명령 보내기예요. 관리자는 역할의 금지 규칙도 건너뛰어요(채팅과 멤버의deny_tools가 적용되지 않아요). 두 가지는 여전히 소유자의 것이에요:- 멤버와 역할 바꾸기: 컴퓨터의
aipager config만 할 수 있고, Telegram이나 Mini App의 어떤 것도 할 수 없어요. - 안전 기준선 건너뛰기(aipager 설정과 봇 token, Claude Code 로그인 정보, aipager의 제어 파일). 관리자의
Grep과Glob은 그 밖의 어디든 검색할 수 있지만, 그런 폴더 가운데 하나이거나, 그 안에 있거나, 그것을 품은 폴더에서는 검색하지 못해요.~,/,/tmp에 대한Grep(또는 홈 폴더의 세션에서 폴더 없이)은 거부되고, 패턴이 그런 폴더에서 시작하는Glob도 그래요(예:/home/you/.config/aipager/*). 프로젝트 폴더 안에서 검색하세요.Grep도구가 없는 Claude Code 버전에서는 Claude가 Bash로 검색해요. 거기서도 홈 폴더 전체나/를 재귀적으로 검색하거나 나열하는 것(grep -rn x ~,rg x $HOME,find / -name x)은 최선의 노력으로 거부되고(홈 폴더나 그 아래에서 실행하는, 폴더가 없거나..인 검색은 보이지 않아요), 프로젝트 안의 검색은 영향을 받지 않아요. 프로젝트 안에서 홈 경로라는 글을 찾는 것(grep -rn "$HOME" src)도 마찬가지예요(보안).
관리자에게는 여전히 Bash가 있고, shell이 있으면 최소 권한의 명령 패턴은 경계가 아니라 최선의 노력일 뿐이에요. 이 컴퓨터의 shell을 줘도 되는 사람만 관리자로 만드세요.
- 멤버와 역할 바꾸기: 컴퓨터의
-
user: 금지 규칙이 적용돼요. Bash도, 다른 코드 실행 도구도 없고(PowerShell, Monitor, 예약된 메시지, workflow. 목록은
safety.CODE_EXECUTION_TOOLS예요), 다른 세션에SendMessage도 못 보내고, worktree도 바꿀 수 없어요. 파일은 세션의 폴더와 세션의 Claude Code scratchpad 안에만 쓰고, 그 폴더의.claude/,.git/,.mcp.json에는 절대 쓰지 않아요(하나하나가 명령을 실행해요).Grep과Glob은 그 폴더들 안에서만, 단순한 상대 glob으로만 검색해요.user는 채팅의 루트 폴더 안에서만 세션을 시작하고, 재개하고, 재시작하고, 모드를 바꾸고, 내 홈 폴더에서 그 폴더 밖의 것은 읽지 못해요. 세션 폴더가 내 홈 폴더,/, 그 위의 폴더이거나, 루트 폴더 밖이라면(루트 폴더가 생기기 전에 시작한 세션, 또는 다른 사람이 시작한 세션) 세지 않아요. 거기서user는 scratchpad에서만 쓰고 검색할 수 있어요. 읽기는 보호된 경로 규칙을 따르고, 로그인 정보가 든 내 파일은 절대 읽을 수 없어요.~/.ssh,~/.gnupg,~/.aws,~/.config/gh,~/.git-credentials,~/.netrc,~/.docker/config.json,~/.kube,~/.config/gcloud,~/.azure,~/.password-store,~/.pgpass,~/.npmrc,~/.pypirc예요. 슬래시 명령: 명령어 키보드의 명령,/compact, 모델 변경만이에요(메시지는 누구의 권한으로 실행되나요 참고). -
read_only: 관찰자. 그룹의 모든 것을 볼 수 있고,
/status,/diff,/whoami,/help,/start,/app을 쓰고,/settings를 보고, 다른 사람이 연/resume목록을 넘겨 볼 수 있어요. 자기 글 메시지, 음성 메시지, 파일은 어디에도 보내지지 않아요. 한 채팅에서 처음 보내는 메시지에는 역할이read_only라고 알려 주는 답이 와요. 그 뒤로 봇은 aipager가 재시작할 때까지 그 채팅에서 그 사람에게 조용해요(답도 반응도 없어요). 혹시 그 사람의 메시지가 세션에 닿을 경우를 위해user와 같은 도구와 쓰기 제한을 받아요.
직접 만드는 역할#
policy.yaml은 기본 역할의 어떤 필드든 바꾸거나 새 역할을 정의할 수 있어요. 내가 설정한 필드가 기본값을 대신해요. 필드는:
can_prompt,can_approve: 메시지 보내기, Claude에게 답하기.can_manage: 전체 권한 모드 쓰기, 그룹의/settings바꾸기,/update실행, 음성 메시지 기능 설치(기본값:owner와admin은 true,user와read_only는 false). 누구에게도 멤버나 역할을 바꾸게 하지 않고, 안전 기준선을 건너뛰지도 않아요.bypass_role_denies,bypass_safety: 위의 표를 보세요.deny_tools,allow_tools,deny_bash_patterns,deny_paths_no_access,deny_paths_no_write: 규칙이 작동하는 방식을 보세요.
처음부터 정의한 역할은 can_prompt와 can_approve가 켜지고, can_manage와 두 건너뛰기 권한이 꺼지고, 로그인 정보가 든 파일이 deny_paths_no_access에 들어 있고, 금지된 도구가 하나도 없어서 Bash가 있는 상태로 시작해요. deny_tools를 설정할 때까지요(그 역할을 가진 멤버가 있으면 aipager doctor가 한 번 경고해요). 쓰기와 검색 제한은 bypass_safety나 bypass_role_denies가 없는 모든 역할에 적용돼요. 루트 폴더는 건너뛰기 권한과 상관없이 can_manage가 없는 모든 역할에 적용돼요. bypass_role_denies나 bypass_safety는 있지만 can_manage가 없는, 내가 정의한 역할은 자기 루트 폴더(와 scratchpad) 안 어디든 쓰고 검색할 수 있지만 그 밖의 다른 곳에서는 못 하고, 내 홈 폴더에서 루트 폴더 밖의 것은 읽지 못해요(bypass_safety가 있으면, 루트 폴더 밖에서 실행되는 세션에서는 scratchpad에만 써요). 제한이 없는 것은 소유자와 관리자뿐이에요.
루트 폴더#
채팅마다 멤버를 위한 루트 폴더가 하나 있어요. 그 채팅에서 역할에 can_manage가 없는 멤버(기본 user와 read_only, 그리고 그것이 없는, 내가 정의한 역할)가 여기서 세션을 시작하고 유지해요. 소유자와 관리자는 여기에 묶이지 않아요. 개인 채팅의 루트 폴더는 그 사람의 것이고, 그룹의 루트 폴더는 그룹의 제한된 멤버가 함께 써요. 두 채팅이 폴더 하나를 함께 쓰는 일은 내가 일부러 둘에게 같은 폴더를 주지 않는 한 없어요(설정 마법사가 확인을 요청해요).
- 폴더 정하기.
aipager config는 그룹을 추가하거나can_manage가 없는 역할의 개인 채팅을 추가할 때 기본값을 미리 채워서 물어보고, Edit a scope → Set the members' root folder로 바꿀 수 있어요(재시작 없는 다시 불러오기로 바로 적용돼요). 현재 설정은 채팅마다 루트 폴더를 보여 주고, 정하지 않았으면(default)를 붙여요. - 기본값은 개인 채팅이면
~/aipager-users/<label>(그 사람의 라벨), 그룹이면~/aipager-users/group-<label>이에요. 라벨은 소문자로 바뀌고, 글자, 숫자,.,_,-가 아닌 문자는-가 돼요. 두 채팅이 같은 폴더를 받게 되면 채팅마다 자기 채팅 ID가 붙어요(sara-123456). 멤버가/new나 Mini App의 “새 세션” 양식을 처음 열 때 이 폴더(와 새로 생기는~/aipager-users)가 권한 0700으로 만들어져서 나만 열 수 있어요. 그 폴더에서 세션이 시작되기 전에 aipager가 Claude Code에 신뢰하는 폴더로 표시해 둬서, Claude Code의 “Do you trust this folder?” 질문 때문에 멤버의 첫 세션이 끝나지 않아요. 기본값은 절대 옮겨 가지 않아요.aipager config에서 한 편집(다른 채팅의 루트 폴더, 지운 채팅, 새 라벨)이 어떤 채팅의 기본 폴더를 바꾸게 되면, 설정 마법사가 그 채팅이 지금 가진 폴더를 그 채팅의 루트 폴더로 적어 둬요. - 설정 마법사가 거부하는 것: 상대 경로, 와일드카드,
/, 내 홈 폴더나 그 위의 폴더, aipager의 보호된 폴더나 로그인 정보가 든 파일이거나, 그 안에 있거나, 그것을 품은 폴더(~/.claude,~/.config/aipager,~/.config,~/.ssh, ...), 그리고 Claude Code의 임시 폴더(/tmp/claude-*, 모든 세션의 scratchpad가 있는 곳)예요. 아직 없는 폴더는 괜찮아요. 내 시스템이 프로그램을 실행하는 폴더(~/bin,~/.local/bin,~/.config/systemd,~/.config/autostart, 또는 그 안의 폴더)라면 확인을 요청해요. - 세션 시작하기.
/new와 Mini App의 폴더 페이지는 루트 폴더와 그 바로 안의 폴더를 보여 주고(숨은 폴더와 밖으로 이어지는 링크는 빼요), “기본 폴더” 줄은 없어요. 새 세션은 내가 그 폴더 가운데 하나를 고르지 않으면 루트 폴더에서 시작해요. “새 폴더”는 고른 폴더 안에 폴더를 만들어요. 그 밖의 것은 모두 “지금 역할로는 내 폴더 ~/... 또는 그 안의 폴더에서만 세션을 시작할 수 있어요.”를 받아요. - 그 밖의 세션. 폴더가 루트 폴더 밖인 세션을 재개, 재시작, 새 세션으로 교체, 모드 전환하면 “이 세션은 내 폴더 ~/... 밖에서 작업해요. 그래서 지금 역할로는 이 세션을 재개하거나 재시작하거나 모드를 바꿀 수 없어요. 관리자에게 부탁하세요.”를 받아요. 그런 세션이 실행 중이라면 메시지는 여전히 전달되고, 안전 hook이 작업을 묶어요. 거기서는 scratchpad에만 써요.
- hook이 작업을 어디까지 묶나요. 제한된 멤버의 작업은 세션의 폴더(루트 폴더 안에 있을 때)와 자기 scratchpad에만 쓰고, 내 홈 폴더에서 루트 폴더 밖의 것은 읽거나
Grep,Glob하지 못하며(자기 scratchpad와 자기 답장 파일은 예외), 다른 채팅의 루트 폴더에는 그것이 어디 있든 절대 닿지 않아요. 내 홈 폴더 밖의 폴더(/usr,/etc, ...)는 여전히 읽을 수 있고, 보호된 폴더는 여전히 거부되며,Bash는user에게 여전히 금지돼요. 한 작업에 루트 폴더가 다른 두 멤버가 섞이면 더 좁은 쪽이 이겨요. 서로 포함하지 않는 두 폴더라면 세션 자신의 폴더만 남아요.
user에게 Bash를 돌려주려면(shell을 줘도 되는 사람에게만) allow_tools에 적거나(그러면 그것이 그 역할의 허용 목록도 돼요) deny_tools를 바꾸세요:
roles:
user:
# 기본 목록에서 Bash를 뺀 것
deny_tools: [PowerShell, Monitor, REPL, Workflow, CronCreate,
RemoteTrigger, AppifactRepl, self_hosted_runner_spawn_local,
self_hosted_runner_requeue_session, SendMessage,
EnterWorktree, ExitWorktree]
그러면 aipager doctor가 경고해요: role user has Bash: its safety rules are best-effort, not a boundary. 이 기본값이 생기기 전에 내 policy.yaml이 user의 deny_tools를 이미 설정했다면, 내 목록이 여전히 기본값을 대신하니까 다시 추가할 때까지 그 역할에 Bash가 있어요. doctor의 경고가 알려 줄 거예요. 이 제한이 무엇을 다루지 않는지는 보안 → 팀 모드의 규칙 적용을 보세요.
그룹에서 봇과 대화하기#
봇이 그룹에서 읽는 것#
그룹에서 aipager는 다음 메시지만 처리해요:
- 명령:
/status,/x1 fix the tests, 또는 명령 뒤에@aipagerbot이 붙은 형태(/x1@aipagerbot). 메뉴에서 명령을 고르면 Telegram이 붙여요. 다른 봇에게 보낸 명령(/x1@otherbot)은 무시해요. - 봇의 메시지에 대한 답장;
- 봇을 멘션한 메시지:
@aipagerbot fix the tests; - 그룹에서 봇의 키보드를 누른 것. 그룹에서는 키보드의 모든 버튼이 작은 네모로 시작하고(
▫️ 중지,▫️ 대화 지우기), 이 표시가 있는 글만 누름으로 쳐요. 그냥 입력한 글은 보통 대화예요. 멤버가중지,대화 지우기,Opus, 세션 이름을 입력해도 아무것도 바뀌지 않아요. 키보드 없이 일부러 키보드 단어를 쓰려면 봇을 부르세요.@aipagerbot 중지, 또는 봇의 메시지에 답장으로중지를 보내세요.
그 밖의 것(멤버끼리의 대화, 봇에 대한 답장이 아닌 사진이나 음성 메시지)은 반응도 답도 받지 않아요. 봇이 그룹 관리자이든 아니든 그래요. 편집한 메시지는 어디서나 무시해요. 메시지를 편집해도 아무 일도 일어나지 않으니 다시 보내세요.
사진, 파일, 음성 메시지도 마찬가지예요. 봇에 대한 답장으로 보내거나 캡션에서 봇을 멘션하세요. 앨범은 캡션을 첫 항목에 다세요. 캡션, 멘션, 답장이 있는 항목 뒤에 도착한 항목은 받고, 그 앞의 항목은 받지 않아요.
멘션은 빠져요. @aipagerbot fix the tests는 Claude에게 fix the tests로 가요(Claude Code는 @word를 파일로 읽을 테니까요). @aipagerbot status와 @aipagerbot x2는 개인 채팅에서 status나 x2를 입력한 것처럼 동작하고, @aipagerbot만 보내면 내 메시지가 어디로 가는지 알려 줘요. 멘션 뒤의 세션 명령은 여전히 되지만(@aipagerbot /x1 fix it), aipager 자체의 명령은 안 돼요. @aipagerbot /stop은 “봇을 멘션하지 말고 /stop 명령만 따로 보내세요.”를 받아요.
멤버만. 그룹 멤버가 아닌 사람은 답(“🚫 이 채팅의 허용 목록에 없어요.”)과 함께 나에게 건넬 자기 Telegram ID를 받고, aipager config를 위해 기록돼요. 그 뒤로 봇은 aipager가 재시작할 때까지 그 사람을 무시해요. read_only 멤버는 자기 역할을 한 번 안내받아요(역할 참고).
익명 관리자는 자기 이름으로 올려야 해요. “Remain anonymous”를 켠 그룹 관리자, 또는 그룹이나 채널 이름으로 올린 메시지는 누가 썼는지 알려 주지 않아요. Telegram이 모든 익명 관리자에게 같은 보낸 사람을 보여 주니까요. aipager는 그런 메시지에 누구의 역할도 줄 수 없어서 “봇을 쓰려면 본인 이름으로 글을 올려 주세요.”라고 답하고(aipager가 재시작할 때까지 채팅마다 한 번), 그 밖에는 아무것도 하지 않아요. 기다리는 사람으로 기록되는 일도 절대 없고, 설정 마법사도 그 공용 ID를 거부해요. 그룹의 관리자 설정에서 나 자신의 “Remain anonymous”를 끄거나, 내 계정으로 올리세요.
그룹에서 세션과 대화하기#
세션의 메시지(답변, 카드)에 답장하면 내 메시지가 그 세션으로 가요. 또는 /x1 내 메시지를 보내거나, /x1로 시작하는 캡션을 붙이세요.
사람마다 자기 현재 세션이 있어요: 가장 최근에 전환했거나(/x1), 메시지를 보냈거나, 답장했거나, 시작한 세션이에요. 다른 멤버의 세션이 답해도 바뀌지 않고, 다른 사람의 메시지로도 바뀌지 않아요. 멘션, 음성 메시지, 템플릿, 키보드의 명령은 내 현재 세션으로 가고, 키보드의 명령이나 템플릿은 답에 그 세션 이름을 적어요(“🧹 x1 세션으로 보냈어요: /clear”). 이름 없는 /stop, /mode, /kill, /restart, /rename, /delete, /diff는 내 현재 세션에만 작용하고(평소의 확인 단계를 거쳐서), 그것도 명령이 그 세션에 맞을 때만이에요(/stop은 작업 중일 때, /delete는 종료됐을 때). 그렇지 않으면 세션이 하나만 남았어도 항상 선택기를 보여 줘요. 그 세션이 다른 사람의 것일 수 있으니까요. /clearqueue와 /now는 내 현재 세션에 작용해요. /kill x1처럼 세션 이름이 있는 명령은 예전처럼 그 세션에 작용해요.
- 아직 내 현재 세션이 없고 실행 중인 세션이 둘 이상이면, 봇이 “어느 세션인가요? 그 세션의 메시지에 답장하거나 /x1 뒤에 메시지를 붙여 보내세요.”라고 세션마다 버튼을 하나씩 붙여 묻고, 아무것도 보내지 않아요. 실행 중인 세션이 하나뿐이면 메시지는 거기로 가요.
- 내 현재 세션이 끝났으면 메시지는 실행 중인 유일한 세션으로 가지 않아요(다른 사람의 것일 수 있으니까요). 봇이 “x1 세션이 종료됐어요. 어느 세션으로 할까요?”라고, 실행 중인 세션마다 버튼을 하나씩, 그리고 x1을 재개할 수 있으면 “▶️ x1 재개”를 붙여 답하고, 아무것도 보내지 않아요.
- 이 두 질문에서 세션 버튼을 누르면, 질문이 가리키는 메시지(글 메시지, 음성 메시지의 받아쓴 글, 또는 파일)도 마치 거기서 보낸 것처럼 그 세션으로 보내고, 질문은 “x2 세션에 보냈어요.”로 바뀌어요. 버튼은 메시지를 보낸 사람만, 10분 동안, aipager가 재시작하기 전까지만 쓸 수 있어요. 그 뒤에는 버튼이 그 세션을 내 현재 세션으로 만들기만 해요(“이 메시지는 너무 오래돼서 보내지 않았어요. 다시 보내 주세요.”). ▶️ 재개는 x1을 되살리지만 아무것도 보내지 않아요. 메시지를 다시 보내세요.
- 그룹에서는 고정된 표시줄에 “메시지는 … 세션으로 가요” 줄이 없어요. 사람마다 다르니까요.
답을 기다리는 카드는 사람마다 따로예요. 새 세션 카드에는 “이 메시지에 답장으로 이름을 보내 주세요. 원하면 먼저 할 일도 함께 적어 주세요:”가, 이름 바꾸기 질문에는 “이 메시지에 답장으로 새 이름을 보내 주세요.”가 적혀 있어요. 그룹에서는 카드마다 누구의 것인지 보여 주고(“🆕 새 세션 (@alice)”), 그 사람만 답할 수 있으며, 두 사람이 동시에 세션을 시작하거나 이름을 바꿀 수 있어요. 다른 멤버가 누군가의 새 세션 카드에 답장하면 “이 카드는 @alice 님의 카드예요. 본인 카드를 받으려면 /new를 보내세요.”를 받고, 어디로도 가지 않아요. 이름 바꾸기 질문은 10분 뒤나, 그 주인이 다른 일을 하면 더 기다리지 않아요. “x1 준비 완료” 카드와 /x1, /start, /help에 대한 답은 세션의 메시지에 답장하거나 봇을 멘션하라고 알려 줘요(“x1 세션의 메시지에 답장하면 (또는 @aipagerbot 멘션) 그 세션과 대화할 수 있어요.”). 보통 메시지는 봇에 닿지 않으니까요.
키보드도 사람마다 따로예요. 템플릿, 명령어, 모델 ›, « 뒤로는 누른 사람의 키보드만 바꿔요. 봇은 그 사람의 누름에 답장하는 방식으로 답하고, 다른 사람의 키보드는 바뀌지 않아요. « 뒤로는 다른 멤버가 무엇을 보든 상관없이 내가 있는 곳에서 한 단계 위로 가요. /start가 보내는 메인 키보드는 그룹 전체의 것이고, 모두를 메인 키보드로 되돌려요. 그룹의 세션이 시작하거나 끝나면 aipager는 그 메인 키보드를 그룹에 한 번 다시 보내서 세션 버튼이 항상 맞게 해요. 표시가 있는 버튼이 생기기 전 버전에서 업그레이드한 뒤에는 예전 키보드의 버튼을 눌러도 무시될 수 있어요. /start를 한 번 보내서 그룹이 새 키보드를 받게 하세요. 개인 채팅의 키보드에는 표시가 없고 예전처럼 동작해요.
Mini App#
/app은 개인 채팅에서만 Mini App을 열어요. 그룹에서는 / 메뉴에 없고, 거기서 입력하면 봇과의 개인 채팅이 이미 설정된 멤버에게는 “📱 Mini App은 개인 채팅에서만 쓸 수 있어요. 봇에게 개인 메시지를 보낸 뒤 거기서 /app을 보내세요.”라고, 다른 사람에게는 “Mini App은 봇과의 개인 채팅에서 열려요. 봇 소유자에게 추가해 달라고 하세요.”라고 알려 줘요.
Mini App은 한 번에 채팅 하나를 보여 줘요. 여러 채팅에 속한 사람(예: 자기 개인 채팅과 그룹)은 맨 위에 채팅 이름 줄이 생겨서 오갈 수 있어요. 개인 채팅으로 열리고, 없으면 그 사람 이름이 있는 첫 채팅으로 열리며, 그 휴대폰에서 마지막 선택을 기억해요. 그 안의 모든 것(세션, 그 버튼, 설정, 앱이 올리는 알림)은 고른 채팅의 것이고, 그 채팅에서의 그 사람 역할을 써요. 채팅이 하나라면 더해지는 것은 없어요.
메시지는 누구의 권한으로 실행되나요#
모든 메시지는 보낸 사람의 권한으로, 그 사람이 보낸 채팅에서 실행돼요. 그룹에서는 user이고 자기 개인 채팅에서는 owner인 사람은 aipager.yaml의 채팅 순서와 상관없이 그룹에서 user로 보내고, 누르고, 명령을 실행해요.
- 모든 Telegram 메시지는 보낸 사람의 신원과 규칙을 세션으로 가지고 가요(보안 → 팀 모드의 규칙 적용 참고). Claude는 메시지마다 누가 보냈는지 보고(그룹에서는
[via Telegram · @bob · role:user]), 앞서 세션을 다룬 사람이 아니에요. 메시지를 보낸 사람이 직접 입력했든,/x1, 템플릿, 키보드의 명령을 썼든 세션을 다루는 사람이 돼요. - 보류된 메시지는 보낸 사람을 유지해요. 다른 사람의 작업이 실행 중일 때(그 작업이 시작한 백그라운드 에이전트 포함), 다른 사람의 메시지가 아직 받기를 기다릴 때, 또는 열린 권한 요청이나 질문이 있을 때 새 메시지는 보류되고(👀), 그것들이 끝나면 따로 작업이 되어 자기 보낸 사람의 권한으로 보내져요. 터미널에서 입력한 작업은 소유자의 것으로 세니까, 소유자의 메시지(또는 개인 채팅이라면 그 채팅 자신의 멤버 메시지)만 거기에 합쳐져요. 내 메시지가 내 작업을 기다리는 일은 절대 없어요. 기다리던 작업이 멈추거나 금지된 도구 때문에 막히면 보류된 메시지는 버려져요(🤷). 다시 보내세요.
- 가장 엄격한 규칙이 이겨요. 그래도 서로 다른 사람의 메시지가 한 작업에 들어가면, 그 작업은 그 안의 모든 사람 가운데 가장 엄격한 조합으로 실행돼요. 실행 중인 작업에 합쳐진 메시지는 절대 작업을 넓히지 않아요. 작업의 남은 부분은 합쳐진 모든 사람 가운데 가장 엄격한 규칙을 유지해요.
- 제한된 역할의 슬래시 명령. 역할 규칙을 건너뛸 수 없는 역할(
user,read_only,bypass_role_denies없이 내가 정의한 역할, aipager가 모르는 보낸 사람)은 명령어 키보드의 슬래시 명령(keyboard.json이 바꿨다면 내 것),/compact, 모델 변경(/model <name>)만 보낼 수 있어요. 그 밖의 것은 입력하든,/x1 /deliver ...로 보내든, 파일 캡션으로 붙이든,/new x1 /review ...의 첫 메시지로 쓰든, 템플릿으로 쓰든 모두 “이 명령은 관리자만 쓸 수 있어요.”와 🤷를 받고, Claude에게는 아무것도 가지 않아요. 소유자와 관리자는 모든 명령을 보낼 수 있어요. - 제한된 역할은 홈 폴더와 로그인 정보 파일에서 떨어져 있어요.
user나read_only의 작업은 세션 폴더인 내 홈 폴더,/, 그 위의 폴더에 절대 쓰지 않고(scratchpad에만 써요), 역할에 나온 로그인 정보 파일을 절대 읽지 않아요. - 제한된 역할은 자기 루트 폴더에 머물러요.
can_manage가 없는 역할의 작업은 내 홈 폴더에서 채팅의 루트 폴더 밖의 것을 읽지 못하고, 그 밖에 있는 세션 폴더에 쓰지 못해요. 루트 폴더는 메시지가 어느 세션으로 가든, 멤버가 메시지를 보낸 채팅의 루트 폴더예요. - 지워진 멤버는 바로 권한을 잃지만, 실행 중인 작업은 잃지 않아요. aipager가 더 이상 모르는 보낸 사람(메시지가 기다리는 동안 지워진 사람)은 제한된 최소 권한으로 실행돼요. 재시작 없는 다시 불러오기 뒤에는 그 사람의 보류된 메시지가 버려지고, 아직 실행 중인 작업은 계속 실행되지만 끝날 때까지 제한된 최소 권한에 묶여요. 바로 끝내려면
/stop을 보내세요(재시작 없이 다시 불러오기 참고). - 다른 Claude 세션에서 온 메시지는 보낸 쪽의 권한을 가지고 와요. 세션 하나가 다른 세션에 보낸 메시지(Claude Code의
SendMessage)는 aipager가 확인한, 그것을 보낸 작업의 권한으로 실행되고, 받는 세션의 이전 권한으로는 절대 실행되지 않아요. aipager가 확인할 수 없는 메시지는 가장 엄격한 규칙에, 파일 변경 금지와 웹 접근 금지로 실행돼요(보안 → Claude 세션 사이의 메시지 참고). - 막힌 도구는 작업을 멈춰요. 작업이 규칙이 금지하는 도구를 쓰려고 하면 작업은 “🛑 x1 · 안전 규칙으로 차단됨: ... (중지됨)”과 함께 바로 멈추고, 이 작업의 이후 모든 도구 호출도 거부돼요.
누가 무엇을 누를 수 있나요#
모든 버튼은 누르는 순간, 버튼이 있는 채팅에서 누른 사람의 역할을 확인해요. 버튼을 만든 명령을 보낸 사람의 역할은 세지 않아요. (어떤 카드는 누가 요청했는지도 확인해요. 확인 카드와 /new의 카드예요. 아래 참고.)
- 모든 멤버는 볼 수 있어요(VIEW).
read_only멤버는/resume을 넘겨 보고,/status의 종료됨 부분, 세션의 ⋮ 메뉴, 모드나 diff를 열고,/settings를 보고, 메뉴를 닫을 수 있어요. 다룰 수는 없어요. 다른 모든 버튼은 “지금 역할로는 이 채팅에서 할 수 없어요.”라고 답해요. - Claude에게 답하려면
can_approve가 필요해요(APPROVE): 허용, 항상 허용, 거부, 질문의 선택지, 제출, 계속, 그리고 고정된 표시줄의 답하기 버튼이에요.read_only에는 이 권한이 없어요. - 세션을 다루려면
can_prompt가 필요해요(PROMPT): 중지, 다시 시도, 압축, 지금 보내기, 종료, 재시작, 이름 바꾸기, 삭제, 재개, 모드 전환, ✍️ 대화, 모두 지우기, 세션 자체의 설정,/new의 카드예요. aipager가 모르는 버튼도 이 권한이 필요해요. - 전체 권한 모드로 바꾸려면 관리자가 필요해요(MANAGE:
owner,admin, 또는can_manage가 있는 역할,/mode full과 같은 규칙): 전체 권한 모드로 바꾸는 카드의 “네, 전환”과 “중지 후 모드 전환”, 그리고 전체 권한 모드로 재개하기예요. 확인 모드와 자동 모드 사이는 세션을 다룰 수 있는 사람이면 누구나 오갈 수 있어요. 전체 권한 모드인 세션을 재개하면(/resume x1입력, 선택기,/new이름 카드의 재개, 또는 Mini App) 다른 사람에게는 자동 모드로 돌아오고, 답변이 그렇다고 알려 줘요: “♻️ x1 세션을 자동 모드로 다시 열었어요: 전체 권한 모드는 관리자만 쓸 수 있어요.” - 자기 개인 채팅에서는 역할과 상관없이 전체 권한 모드를 쓸 수 있어요(
user포함. 메시지를 보내지 않는 역할인read_only는 제외. 직접 만든 역할은can_prompt와can_approve가 필요해요. 전체 권한 모드는 Claude Code의 모든 권한 요청에 답하니까요). 위의 전체 권한 모드로 들어가는 모든 길을 거기서 쓸 수 있어요./mode full, 모드 카드,/new !x1,/new의 카드,/resume, Mini App이에요. 이것은 그 사람 자신의 개인 채팅에서만 적용돼요. 그룹에서, 그리고 다른 사람의 개인 채팅에서는 전체 권한 모드에 여전히 관리자가 필요해요. 그 사람의 새 세션은 모두와 마찬가지로 자동 모드로 시작하고,/settings→ 새 세션에서 전체 권한 모드를 고를 때까지 그래요. 관리자가 할 수 있는 다른 일은 그 사람에게 열리지 않아요(/update, “음성 메시지 기능 설치” 버튼, 그룹의/settings는 여전히 관리자 전용이에요). 이런 식으로 전체 권한 모드를 주는 것은 안전해요. Claude Code를--dangerously-skip-permissions로 시작하는데, 이 옵션은 Claude Code 자체의 권한 요청만 건너뛰어요. aipager의 PreToolUse hook은 모든 도구 호출에서 계속 실행되고, 확인 모드와 자동 모드에서와 똑같이 멤버의 역할 규칙(안전 기준선, 폴더 제한, 금지 규칙, 로그인 정보가 든 파일)을 적용해요. - 소프트웨어 설치에는 관리자가 필요해요(UPDATE): 음성 메시지 기능의 설치와 재시작 버튼이에요.
/update의 버튼과 같아요. - 그룹의
/settings를 바꾸려면 관리자가 필요해요. 보는 것은 누구나 할 수 있어요. - 확인 카드는 그것을 요청한 사람의 것이에요. 그룹에서 종료(
/kill, ⋮ 세션 종료), 재시작, 삭제, 모드 전환의 확인 카드,/kill,/restart,/delete,/mode,/stop,/rename,/diff가 보여 주는 세션 선택기, 그리고 재개를 누르면 나오는 “x1 세션을 어떤 모드로 재개할까요:” 카드(그 모드 버튼)는 명령을 보냈거나 그것을 만든 버튼을 누른 사람에게만 답해요. 다른 사람이 누르면, 취소까지 포함해서, “@alice 님의 카드예요. 본인 카드를 받으려면 /kill x2 명령을 보내세요.”(그 카드의 명령과 함께)를 받고 아무것도 바뀌지 않아요./mode카드 자체(세션이 지금 어떤 모드인지 보여 줘요)는 모두의 것이에요. 거기서 전환을 누르면 누른 사람의 확인 카드가 만들어져요./new의 카드는/new를 보낸 사람에게만 답해요. 카드의 주인은 카드가 쓰이거나 취소될 때까지, aipager가 재시작해도 기억돼요(최신 카드 4096개)./new의 이름 카드와/rename의 이름 질문은 예외예요. 어차피 10분 뒤에 만료되니까, 재시작한 뒤에는 명령을 다시 보내세요. - 버튼은 자기 세션이 있는 채팅에서만 작동해요. 다른 곳에서 누르면 “이 버튼은 다른 채팅의 버튼이에요.”라고 답하고 아무것도 하지 않으며, 재개는 항상 버튼에 적힌 그 세션을 재개하고, 다른 채팅에 있는 같은 이름의 세션은 절대 재개하지 않아요.
- 오래된 버튼은 아무것도 바꾸지 않아요. 이전 작업에서 남았거나, 세션이 끝났다가 돌아오기 전의 버튼은 지금 실행 중인 것에 작용하지 않고, 그렇다고 알려 줘요(명령 → 오래된 버튼 참고).
모두에게 보이는 것#
Claude에 대한 답. 허용, 거부, 답변은 모두 채팅에, 세션 카드 아래에, 누가 했는지와 함께 올라가요:
✅ x1 · 허용됨 · @alice · Bash: ls -la /tmp
🚫 x1 · 거부됨 · @bob · WebFetch: https://example.com
✓ x1 · 답변함 · @carol · Which database? → Postgres
누가 세션을 바꿨는지. 버튼을 누른 결과는 누른 사람만 보니까, 그룹에서는 세션을 바꾸는 결과마다 누가 했는지 적어요:
⚠️ x1 · @bob 님이 중지했어요
⏹ @bob 님이 x2 세션을 종료했어요
🔄 @alice 님이 [x1] 세션을 재시작했어요.
🗑️ @carol 님이 [x3] 세션을 삭제했어요.
⚙️ @alice 님이 세션을 🤖 자동 모드로 바꿨어요.
@alice 님이 바꿨어요. (/settings 아래)
“x1 준비 완료” 카드는 누가 모드나 모델을 바꿨는지 적어요(“@bob 님이 모델을 바꿨어요: Opus”). 개인 채팅에서는 이런 결과 줄에 아무 이름도 적지 않아요. Mini App에서 한 변경은 채팅에 “Mini App에서 …” 형태로 올라가요(거기서 누른 중지도 세션 카드에 누가 했는지 적어요).
규칙에 막힌 도구. 작업이 “🛑 x1 · 안전 규칙으로 차단됨: Write: 이 역할의 deny_tools에 있어요 (중지됨)”과 함께 멈춰요. Claude Code가 실행 중인 작업을 시작한 메시지를 보낸 사람이 쓸 수 없는 도구를 요청하면, 봇은 요청을 보여 주지 않고 스스로 거부로 답하고, ⛔ x1 · @bob 님에게 Edit 차단됨 (역할 user)를 그 아래에 호출의 요약과 함께, 그 사람 이름을 적어 올려요. 여러 사람의 메시지로 시작한 작업은 그 가운데 누구든 그 도구를 쓸 수 없으면 막히고, 누구인지 적어요. 터미널에서 입력한 작업은 내 것이고 멤버의 규칙에 절대 막히지 않아요. 다만 Telegram 메시지가 합쳐지면 세션의 가장 최근 메시지를 보낸 사람의 규칙으로 확인해요(그 사람을 모르면 그룹 자체의 목록으로). aipager가 작업이 누구의 것인지 모를 때는(aipager가 재시작할 때 이미 실행 중이었던 경우) 세션의 가장 최근 메시지를 보낸 사람의 규칙을 쓰고, 그것도 모르면 그룹 자체의 목록을 써요.
메시지 상태. 메시지의 👀는 세션에 전달됐다는 뜻이에요(또는 보낼 수 있을 때까지 보류 중). 👍는 Claude가 가져갔다는 뜻이고, 🤷는 Claude가 가져가기 전에 버려졌다는 뜻이에요. /whoami는 이 채팅에서의 내 라벨, 역할, 규칙을 보여 줘요.
고정된 상태 표시줄. 봇이 고정 권한이 있는 관리자라면 그룹에는 자기 고정된 상태 표시줄이 있고(명령 → 고정된 상태 표시줄 참고), 이 그룹의 세션만 나열해요. 이 권한이 없으면 그룹에는 상태 표시줄이 없어요. 메시지는 고정되지 않은 채 기록에 남지 않고 지워져요.
디스크의 감사 로그. ~/.claude/aipager-audit.jsonl은 모든 결정을 user_id, username, 채팅과 함께 기록해서, 나중에 각자가 무엇을 했는지 되짚어 볼 수 있어요. 채팅이 어떤 언어를 쓰든 항상 영어로 쓰여요.
언어. 그룹의 aipager 자체 메시지, 카드, 키보드는 한 언어를 쓰고, 관리자가 /language로 정해요. 관리자가 고르기 전까지 그 언어는 영어예요. 누구의 Telegram 앱도 따르지 않아요. 한 사람만 보는 것, 즉 누른 뒤의 짧은 안내는 그 사람 자신의 언어로 보여요. 봇과의 개인 채팅에서 고른 것, 없으면 이 버전 뒤에 들어온 사람이라면 Telegram 앱의 언어, 그것도 없으면 그룹의 언어예요. 바뀌는 것은 aipager의 글뿐이고, Claude는 각자가 쓴 언어로 답해요. 언어를 보세요.
메시지는 어디로 가나요#
- 세션은 채팅 하나에 속하고, 그 세션에 관한 모든 것은 그 채팅으로 가요: 카드, 답변, 권한 요청, 파일, 알림이에요. 한 채팅의 메시지는 다른 채팅의 세션으로 절대 가지 않고, 세션 이름도 자기 채팅에서만 쓸 수 있어요. 다른 채팅의 세션 이름을 입력하면 “여기에는 … 이름의 세션이 없어요”라는 답이 와요. aipager가 실행 중이지만 아직 채팅이 설정되지 않았을 때 시작한 세션은
aipager config가 채팅을 추가하면 바로 자기 채팅을 받아요. 세션에 아직 채팅이 없으면 어떤 채팅도 그것을 나열하거나 다룰 수 없어요. - 터미널에서 시작한 세션(
aipager session <name>)은 소유자의 개인 채팅에 속해요. 멤버가owner역할(bypass_safety가 있는 역할)을 가진 개인 채팅이에요. 개인 채팅이 여럿인데 소유자의 것이 하나도 없으면 목록의 첫 개인 채팅이고, 개인 채팅이 아예 없으면 첫 채팅이에요. 개인 채팅이 있는 설치에서는 절대 그룹으로 가지 않아요. - supergroup 업그레이드는 저절로 따라가요(Telegram이 그룹을 업그레이드할 때 참고).
aipager.yaml의 구조 (팀 관련 부분)#
schema_version: 3
bot_token: "…"
scopes:
- kind: dm
chat_id: 12345 # 내 개인 채팅: 그 ID가 내 사용자 ID예요
label: owner DM
members:
- id: 12345
label: alice
role: owner
- kind: group
chat_id: -100123456789
label: team
deny_tools: [Write, Edit] # 선택 사항: “Block file edits”에 대한 답
root: /home/alice/aipager-users/group-team # 멤버의 루트 폴더
members:
- id: 12345 # Telegram 사용자 ID (라벨이 아니고, 채팅 ID도 아니에요)
label: alice # 채팅에서 이 사람이 보이는 모습 (@alice)
role: owner
- id: 67890
label: bob
role: user
deny_tools: [WebFetch] # 선택 사항, 멤버마다
- id: 11111
label: charlie
role: read_only
개인 채팅 항목과 그룹 항목은 함께 있을 수 있어요. 봇은 나열된 모든 채팅을 서비스하고, 채팅마다의 세션은 따로 유지돼요. 이 파일은 aipager config가 써요. 직접 편집할 일은 없어요.
규칙이 작동하는 방식#
~/.config/aipager/policy.yaml이 역할마다 규칙을 정해요. aipager policy validate로 확인하세요. 역할마다 쓸 수 있는 필드(직접 만드는 역할의 것 말고도):
deny_tools: 묻지 않고 금지하는 도구 이름(예:Write,Edit,Bash,WebFetch).allow_tools: 비어 있지 않으면 허용 목록이에요. 그 밖의 모든 것이 그 역할에 금지돼요.deny_bash_patterns:Bash입력과 맞춰 보는 패턴.deny_paths_no_access/deny_paths_no_write: 경로 규칙.user,read_only, 그리고bypass_role_denies없이 내가 정의한 모든 역할의deny_paths_no_access는 위의 user에 나온 로그인 정보 파일로 시작해요. 이것을 설정하면 그 목록을 대신하니까, 유지하고 싶은 파일은 내 목록에 복사하세요(다른 메시지가 합쳐진 실행 중인 작업은 끝날 때까지 다시 그 목록에 묶여요)./나~로 시작하지 않는 규칙(**/.env)은 어디서든 맞으니까, 그런 규칙이 있으면 제한된 역할의 모든Grep/Glob이 거부되고(어떤 검색이든 그런 파일을 읽을 수 있으니까요), 그러면 그 작업이 멈춰요.
aipager.yaml의 채팅 deny_tools와 멤버 deny_tools는 역할의 것에 더해지고, 멤버의 allow_tools는 역할의 허용 목록에 더해져요. 이것들은 모두 bypass_role_denies가 없는 역할에만 적용돼요. 소유자나 관리자는 영향을 받지 않아요.
~/.config/aipager/policy.d/*.yaml의 파일은 policy.yaml 다음에 이름 순서대로, 같은 형식으로 읽어요. 뒤의 파일에서 설정한 역할 필드는 앞의 값을 대신하고, safety: 목록은 더해지기만 해요. aipager policy validate가 모두 확인해요.
모든 역할 아래에는 owner만 건너뛸 수 있는 안전 기준선(보호된 경로와 명령 패턴)이 있어요. 관리자도 따라야 해요. aipager의 기본 목록에 policy.yaml의 safety: 부분을 더한 것이고, 이 부분은 더하기만 할 수 있어요:
safety:
deny_paths_no_access: [/srv/secrets/**] # 읽기, 쓰기, 검색 금지
deny_paths_no_write: [~/notes/**] # 읽기는 되지만 쓰기는 금지
deny_bash_patterns: ['\bcurl\b'] # 막을 Bash 명령
owner를 뺀 모든 역할의 모든 작업이 이 기준선 전체를 받고, aipager가 누구의 것인지 모르는 작업도 마찬가지예요. aipager doctor --safety-check가 지금 적용 중인 것을 나열해요. 재시작 없는 다시 불러오기 뒤에는 그 안의 변경이 다시 불러온 뒤에 보낸 메시지에 적용되고, 실행 중인 작업이나 대기열에서 기다리는 메시지는 예전 목록을 유지해요(바로 적용하려면 그 작업을 멈추세요). 거기서 /나 ~로 시작하지 않는 deny_paths_no_access 경로는 어디서든 맞으니까, 그런 경로가 있으면 owner를 뺀 모든 역할의 모든 Grep/Glob이 거부돼요(aipager doctor가 경고해요). 보호된 경로는 Claude Code의 파일 도구(Read, Write, Edit, MultiEdit, NotebookEdit, LSP, Grep, Glob)에 적용되고, 제한된 역할의 Grep/Glob은 세션의 폴더 안에 머물러야 해요(~/.config에서 aipager/**를 검색하면 거부돼요). WebFetch는 경로를 확인하지 않아요. SendUserFile(Claude가 채팅으로 보내는 파일)은 Read처럼 확인하고, 멤버는 채팅의 루트 폴더에서만 보낼 수 있어요(보안). aipager가 어떤 보낸 사람에게도 묶을 수 없는 Telegram 작업은 최소 권한을 받고, 이것은 적어도 user만큼 엄격해요. 다시 시도는 다시 시도하는 메시지를 누른 사람이 보냈다면 누른 사람의 권한으로, 아니면 최소 권한으로 실행돼요. 명령 패턴은 Bash가 있는 역할에만 의미가 있고, 거기서는 벽이 아니라 거름망이에요. 보안 → 팀 모드의 규칙 적용을 보세요.
개인정보에 관해 생각할 것#
- 봇은 설정된 채팅만 들어요. 봇을 다른 그룹에 추가해도 거기서 켜지지 않고, 거기서는 절대 답하지 않아요. 거기서 봇을 부른 사람(그 사람의 ID, 사용자 이름, 이름, 그리고 그룹의 ID와 제목)을
~/.claude/aipager-pending-users.json에 기록해서aipager config가 그 그룹을 추가할 수 있게 해요. 이 파일은 나만 읽을 수 있고, 지난 30일 동안 나타난 최신 200명을 보관해요. read_only멤버는 메시지, 답변, 도구 입력을 읽을 수 있어요. 다룰 수는 없지만 그룹의 모든 것을 봐요. 누군가에게 숨겨야 하는 일이 있다면 그 사람은 그룹에 있으면 안 돼요.- 개인 채팅이 있는 설치에서는 터미널 세션과 내 개인 채팅의 세션이 그룹에 절대 나오지 않아요(메시지는 어디로 가나요 참고). 예전 aipager가 그룹으로 옮긴 터미널 세션은 그룹에 남아요. 종료하고, 그룹의 종료된 세션에서 지운 다음, 다시 시작하세요.
aipager.yaml은 권한 0600(소유자만)이에요. aipager는policy.yaml의 권한을 정하지 않아요. 직접 소유자 전용으로 유지하세요(chmod 600 ~/.config/aipager/policy.yaml).- 감사 로그(
~/.claude/aipager-audit.jsonl)에는 따로 권한이 없어요. 내~/.claude폴더만큼 비공개예요.
사용자의 권한 거두기#
aipager config→ Edit a member → 그 채팅 → 그 사람 → Remove member를 실행하세요. 메시지 보내기 권한만 거두고 읽기는 계속하게 하려면 Set role로read_only를 고르세요. (나중에 다시 추가하려면: Edit a scope → 그 그룹 → Add a member.) 누군가의 개인 채팅이라면 먼저 그 세션을 종료하고, Edit a scope → 그 사람의 개인 채팅 → Remove this scope를 하세요.- 설정 마법사가 aipager를 재시작 없이 다시 불러오고 "Scopes reloaded live"를 보여 줘요. 그 사람의 보류된 메시지는 바로 버려지고(🤷와 “Claude에게 보내지 않았어요” 답), 아직 실행 중인 작업은 끝날 때까지 제한된 최소 권한에 묶여요. 그 작업을 바로 끝내려면
/stop을 보내세요. - Telegram 그룹에서도 그 사람을 내보내세요. 그룹에 있는 한 그 사람은 거기 올라오는 모든 것을 여전히 읽을 수 있어요. 카드, 답변, 도구 입력, 권한 요청이에요.
1단계 뒤에 설정 마법사가 무엇이라고 하는지 보세요. aipager를 재시작하라고 하면 aipager에 연결하지 못한 거예요. 재시작하세요(aipager service stop, 그다음 aipager service start). 그때까지는 예전 멤버 목록이 메모리에 남아 있어요. "Not applied"라고 하면 알려 준 문제를 고치세요. 그때까지 aipager는 예전 설정을 유지해요. aipager.yaml을 직접 편집한 뒤에는 다시 불러오기 지시를 직접 보내세요(재시작 없이 다시 불러오기 참고).