Anil-matcha/open-dots
요약
Open Dots는 OpenAI Dots, Claude Cowork 등 유료 AI 에이전트 제품을 대체할 수 있는 오픈 소스 자가 호스팅 대안입니다. 이 프로토타입은 특히 코딩 에이전트 워크플로우에 중점을 두어, Telegram이나 웹에서 Claude Code 작업을 실행하고 위험한 동작 전에 사용자 승인을 요청하는 기능을 제공합니다.
핵심 포인트
- 유료 AI 에이전트를 대체할 수 있는 오픈 소스 자가 호스팅 솔루션입니다.
- Claude Code 작업 실행 및 GitHub 연결을 지원하며, 위험 동작 전 검토 과정을 거칩니다.
- 사용자 자격 증명을 암호화하여 저장하고 PostgreSQL로 모든 기록을 관리합니다.
- 개발 환경 구축 시 Boat API 키와 Telegram 봇 토큰 등이 필요합니다.
Open Dots는 OpenAI Dots, Meta Muse, Grok Bot, Instinct, Manus Cue, Claude Cowork, ChatGPT agent와 같은 유료 AI 에이전트 제품을 탐색하는 사람들을 위한 오픈 소스 자가 호스팅 대안입니다. 현재 프로토타입은 코딩 에이전트 워크플로우에 중점을 두고 있습니다: Telegram이나 웹에서 Claude Code 작업을 실행하고, 위험한 동작을 검토하며, GitHub를 연결하고, 반복적인 프롬프트를 예약합니다. 이 서비스들은 기능 면에서 다르며, 모든 기능을 대체하는 것은 아닙니다.**
두 인터페이스 모두 사용자별로 동일한 API, 샌드박스 및 자격 증명을 사용합니다.
상태: 프로토타입 / 활발히 개발 중. 다른 사람을 위해 실행하기 전에 알려진 제한 사항(Known limitations)을 확인하십시오.
-
기능 (What it does)
-
Open Dots를 사용하는 이유 (Why Open Dots)
-
빠른 시작 (Quick start)
-
Open Dots 사용하기 (Using Open Dots)
-
작업 예약 (Scheduling tasks)
-
권한 요청 프롬프트 (Permission prompts)
-
아키텍처 (Architecture)
-
프로젝트 레이아웃 (Project layout)
-
알려진 제한 사항 (Known limitations)
-
기여 (Contributing)
-
Telegram 또는 브라우저에서 일회성 샌드박스에 코딩 에이전트 작업을 실행합니다.
-
Claude를 OAuth 로그인 흐름을 통해 연결하고, 선택적으로 사용자의 GitHub 계정을 연결합니다.
-
파일 쓰기, 셸 명령어, Git 푸시와 같은 위험한 동작 전에 승인을 요청합니다.
-
웹 UI에서 동일한 Claude 대화에서 완료된 작업을 계속 진행할 수 있습니다.
-
반복적인 프롬프트를 예약하고 Telegram에서 실행 결과를 받습니다.
-
사용자 자격 증명을 암호화하여 저장하고 PostgreSQL에서 샌드박스, 작업, 권한 및 스케줄 기록을 관리합니다.
Open Dots는 개인과 개발자에게 코딩 작업, 가시적인 승인 단계, 앱 및 데이터에 대한 로컬 제어를 할 수 있는 검사 가능한 작업 공간을 제공합니다. 웹 UI는 Telegram 없이 게스트 사용을 지원하며 Telegram ID와 연결할 수도 있습니다.
-
Python 의존성 관리를 위한 uv
-
웹 UI를 위한 Node.js (18+) 및 npm
-
Docker (Postgres용)
-
scripts/dev.sh를 사용하는 Bash (Windows에서는 Git Bash로도 잘 작동합니다) -
샌드박스 생성을 위한 Boat API 키가 필요합니다. 만약 이 키가 유료 플랜으로 조직/팀을 보유한 개인 계정에 속한다면, 해당 조직의 ID(BOAT_ORG_ID 참조)도 필요합니다.
below) — 그렇지 않으면 샌드박스 생성 시 개인 계정에 청구되고 402 Payment Required가 반환됩니다.
- @BotFather에서 받은 Telegram 봇 토큰 (봇을 실행하거나 웹 UI를 Telegram에 연결하려는 경우에만 필요합니다. 웹 UI의 게스트 모드는 이것 없이 작동합니다)
- 샌드박스가 권한 결정을 위해 백엔드로 콜백할 수 있는 공개 URL (권한 프롬프트 참조) — 로컬 개발 환경에서는 이를 ngrok 또는 유사 서비스로 백엔드를 터널링해야 합니다. 앱을 실행하는 데는 필요하지 않지만, 파일을 쓰거나 셸 명령 등을 트리거하여 위험한 작업을 수행하는 경우 실제로 결정을 받기 위해 필요합니다.
cp backend/.env.example backend/.env
backend/.env에 내용을 채우세요.
| 변수 (Var) | 필수 여부 (Required) | 설명 (Notes) |
|---|---|---|
BOAT_API_KEY | 예 (yes) | Boat 대시보드에서 가져옵니다. |
BOAT_BASE_URL | 아니오 (no) | 기본값은 https://boat.dev/api/v1입니다. |
BOAT_ORG_ID | 위 참고 사항 참조 | 샌드박스 생성 시 조직(org)에 연결하여 개인 계정이 아닌 조직에 청구할 팀/조직 ID (예: team_...)입니다. |
TOKEN_ENCRYPTION_KEYS | 예 (yes) | 저장된 자격 증명을 암호화하는 데 사용되는 Fernet 키입니다. 다음 명령어로 생성하세요: uv run python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" . 로테이션을 지원하기 위해 여러 키를 쉼표로 구분하여 입력합니다. |
TELEGRAM_BOT_TOKEN | 봇/Telegram 연결 시에만 | @BotFather에서 가져옵니다. |
TELEGRAM_BOT_USERNAME | 아니오 (no) | 사용자님의 봇 @username입니다 (@ 제외). 웹 UI에서 클릭 가능한 링크로 표시됩니다. 장식용일 뿐입니다. |
FRONTEND_ORIGINS | 아니오 (no) | 브라우저에서 API를 호출할 수 있도록 허용되는, 쉼표로 구분된 오리진(origin) 목록입니다. 기본값은 http://localhost:3000입니다. |
HOOK_TOKEN | 예 (yes) | 권한 후크의 호출자(샌드박스)가 제시해야 하는 공유 비밀 값으로, 사용자님의 백엔드 자체 샌드박스만 권한 결정을 제출할 수 있도록 합니다. 다음 명령어로 생성하세요: python -c "import secrets; print(secrets.token_urlsafe(32))" |
PERMISSION_HOOK_BASE_URL | 예 (yes) | 위의 ngrok 또는 유사 서비스 사전 요구 사항에서 얻은 공개 URL이며, 후행 슬래시(trailing slash)가 없어야 합니다 — 예: https://your-tunnel.ngrok-free.app. |
각 샌드박스(sandbox)의 .claude/settings.json에 작업 시작 시 자동으로 포함됩니다. | POSTGRES_* | 아무것도 안 함 | 기본값은 backend/docker-compose.yml을 따릅니다. |
웹 UI(frontend/)는 자체적인 .env.example 파일을 가지고 있으며, 이는 scripts/dev.sh가 첫 실행 시 자동으로 .env.local로 복사합니다. 만약 프론트엔드를 별도로 실행하는 경우 이 파일을 직접 복사하고, 기본값을 사용하지 않는다면 NEXT_PUBLIC_API_BASE_URL을 백엔드 주소로 지정해야 합니다.
./scripts/dev.sh
리포지토리 루트에서 이 스크립트를 실행하면 Docker를 통해 Postgres를 시작하고, 상태가 정상인지 기다린 후, 백엔드 및 프론트엔드 의존성을 설치하고, 마이그레이션(migrations)을 실행한 다음, FastAPI 앱(http://127.0.0.1:8000, 문서(docs)는 /docs에서 확인 가능)과 Telegram 봇, 그리고 웹 UI(http://127.0.0.1:3000)를 시작합니다. Ctrl+C를 누르면 시작된 모든 것(Postgres는 계속 실행됨 — docker compose -f backend/docker-compose.yml down을 사용하여 중지)이 멈춥니다.
필요하지 않은 부분 건너뛰기:
./scripts/dev.sh --no-bot # API + 웹 UI만
./scripts/dev.sh --no-frontend # API + 봇만
cd backend
docker compose up -d # Postgres
uv sync # 의존성 설치
...
Telegram 봇은 사용자에게 다음 과정을 안내합니다:
/connect
— 샌드박스를 생성하고 에이전트 제공업체(현재 Claude)에 대한 OAuth 로그인을 그 안에서 시작합니다. — OAuth 코드를 붙여넣으면, 봇은 결과로 나온 자격 증명 파일(credential file)을 샌드박스 외부에서 읽어 암호화한 후 저장합니다.
/connect_github
(선택 사항) — 사용자의 GitHub 계정을 디바이스 플로우 로그인(device-flow login)을 통해 연결하여, git push와 같은 작업이 공유/익명 신원이 아닌 해당 사용자로서 수행되도록 합니다. 채팅창에 토큰을 직접 입력할 필요가 없습니다: 봇이 일회용 코드를 게시하고 github.com/login/device 링크를 제공하면, 사용자가 브라우저에서 이를 승인하고 봇이 그 결과를 자체적으로 가져옵니다. — 일반 텍스트 메시지: 사용자의 샌드박스 내에서 작업으로 실행하고 완료되면 결과로 응답합니다. 만약 사용자가 GitHub를 연결했다면, 이 자격 증명은 작업이 실행되기 전에 샌드박스로 복원됩니다.
/schedule
— 프롬프트를 크론 스케줄에 따라 반복 실행합니다(시간별/일별/평일/주간, 항상 UTC 기준) 한 번만 실행하는 대신. /schedules
등록된 스케줄을 나열하고, /pause로 일시 중지하며, /resume와 /unschedule로 관리할 수 있습니다.
각 실행 결과는 채팅에 보고됩니다 — 성공 시 결과가 표시되고, 실패 시 오류가 표시되며, (예: 만료된 로그인) 지속적으로 재시도하는 대신 자동으로 일시 중지되는 스케줄은 확인할 수 있습니다. 아래에서 예약 작업(Scheduling tasks)을 확인하세요.
웹 UI에서도 동일한 기능을 브라우저를 통해 사용할 수 있습니다:
시작하기 (Get started) — 게스트로 계속하거나(로컬 ID, Telegram 필요 없음) Telegram 연결: 페이지가 일회용 코드를 생성하면, 해당 코드를 봇에게 /link <코드> 형식으로 보내면 브라우저가 실제 Telegram 사용자 ID와 연결됩니다. Claude 연결 (Connect Claude) — /connect와 동일한 OAuth 흐름이지만, 채팅 메시지 대신 REST 엔드포인트를 통해 작동합니다(샌드박스 가져오기 또는 생성, 로그인 시작, 코드를 페이지에 다시 붙여넣기). GitHub 연결 (Connect GitHub) (선택 사항) — /connect_github와 동일한 디바이스 플로우 로그인: 페이지가 일회용 코드와 github.com/login/device 링크를 보여주며, 사용자가 승인하면 자동으로 결과를 가져옵니다 — 코드를 다시 붙여넣을 필요가 없습니다. 작업 제출 (Submit a task) — 같은 POST /api/v1/tasks 엔드포인트로 전송하고, 결과에 대해 폴링하며, 해당 사용자에게 과거 작업을 나열하는 프롬프트 상자입니다. 완료된 작업은 새로운 관련 없는 대화를 시작하는 대신 제자리에 답장하여 동일한 Claude 대화를 계속할 수 있습니다. '스케줄에 반복'을 체크하면 같은 프롬프트가 주기적인 스케줄로 변환됩니다 (POST /api/v1/schedules) — 브라우저 자체 시간대의 빈도(시간별/일별/평일/주간)와 시간을 선택합니다. 스케줄은 작성기 아래에 표시되며 일시 중지/재개/삭제 제어 기능이 있습니다. 예약 작업(Scheduling tasks)을 확인하세요.
Telegram에서:
/start
/connect
— 샌드박스를 생성하고(시간이 좀 걸릴 수 있음) 로그인 URL을 답장합니다. 이를 열고 OAuth 흐름을 완료한 후, 코드를 채팅에 다시 붙여넣으세요./connect_github
(선택 사항) — 답장에 코드와 링크가 포함됩니다. 링크를 열고 코드를 입력한 후 승인하세요. 채팅에 아무것도 보낼 필요는 없습니다. - 'hello라고 적힌 웹페이지 생성' 같은 메시지를 보내보세요.
— 봇이 이를 사용자의 샌드박스에서 실행하고 결과(또는 오류)를 답장합니다. /schedule daily 08:00 generate my report
— 해당 프롬프트를 매일 08:00 UTC에 반복합니다. /schedules
으로 확인하고, /pause 1, ,/resume 1, ,/unschedule 1로 관리할 수 있습니다. 이들은 봇이 제공한 ID를 통해 관리됩니다. 실제로 실행되려면 API 프로세스가 실행 중이어야 합니다 — 스케줄링 작업(Scheduling tasks)을 참조하세요.
웹 UI(http://localhost:3000)에서는:
시작하기(Get started) — 독립적인 게스트 세션을 위해 'Telegram 없이 계속하기(Continue without Telegram)'를 클릭하거나, '대신 Telegram 연결하기(Link Telegram instead)'를 클릭하고 봇에게 /link <코드>를 전송하세요 (페이지가 이를 자동으로 감지합니다).Claude 연결하기(Connect Claude) — '로그인 시작(Start login)'을 클릭하고 링크를 열어 코드를 페이지에 붙여넣으세요.GitHub 연결하기(Connect GitHub) (선택 사항) — 'GitHub 연결하기(Connect GitHub)'를 클릭하고 링크를 열어 표시된 코드를 입력하세요. 페이지로 돌아올 필요가 없습니다 — 승인하면 업데이트됩니다.작업 제출하기(Submit a task) — 프롬프트를 입력하고 제출하세요. 페이지는 결과를 폴링하고 아래에 과거 작업을 나열합니다. 완료된 작업을 클릭하여 확장하고 같은 대화에서 답장할 수 있습니다. - **'이것을 스케줄로 반복(Repeat this on a schedule)'**을 확인하고, '매일(Daily)'과 몇 분 뒤의 시간을 선택한 후 '스케줄 생성(Create schedule)'을 클릭하세요. 이는 아래의 스케줄 목록에 다음 실행 시간과 함께 나타나며, 거기에서 일시 중지/재개/삭제할 수 있습니다.
스케줄(schedule 테이블)은 저장된 프롬프트와 cron 표현식 및 시간대입니다. API 프로세스 — 봇 프로세스가 아닌 — 의 세 가지 백그라운드 루프가 이를 구동합니다 (app/core/background.py, app/services/schedule_service.py, 모두 30초 간격으로 작동):
— poll_due_schedules를 통해 next_run_at이 지난 스케줄을 가져와 각 발생에 대해 Tasks 행을 생성합니다 (FOR UPDATE SKIP LOCKED, 여러 API 인스턴스가 있어도 안전함), 그런 다음 이를 시작합니다. — 아무도 폴링하지 않는 작업을, 스케줄 실행처럼 완료합니다 (아무도 sync_active_tasks를 지켜보지 않음).
for it). 또한, 실행 도중 충돌로 인해 고아화된(orphaned) 작업도 복구합니다.— 각 완료된 예약 실행에 대해 정확히 한 번 보고됩니다: 성공 시 결과를 전송하고, 실패 시 오류를 전송하며, 자체적으로 성공할 수 없는 경우(만료된 로그인, 누락된 자격 증명 등) 또는 연속 3회 실패 후에는 일정을 일시 중지합니다. report_finished_runs을 통해 Telegram으로 전달되며, 숫자 형태의 (Telegram 연결) user_id로 식별된 사용자에게만 전송됩니다.
웹 전용 게스트는 대신 스케줄 목록에서 결과를 확인합니다.
이러한 루프가 API 프로세스 내에 존재하기 때문에, 실제로 일정이 실행되려면 API가 실행 중이어야 합니다 — Telegram에서 생성하는 경우 봇 프로세스가 스케줄을 실행할 필요는 없지만 (단지 행만 작성함), uv run fastapi dev app/main.py (또는 ./scripts/dev.sh)를 실행하지 않으면 아무것도 작동하지 않습니다.
웹 UI에서 생성된 일정은 브라우저 자체의 시간대를 사용하며, 이는 생성 시점에 기록됩니다. Telegram에서 생성된 것은 항상 UTC입니다. 왜냐하면 Telegram은 봇에게 사용자 시간대를 읽을 방법이 없기 때문에 — 봇의 답변은 항상 시간을 UTC로 명시하기 때문입니다.
AI가 샌드박스 내에서 시도하는 모든 위험한 동작(파일 쓰기, 셸 명령어 실행, git push 등)은 실행되기 전에 PermissionRequest 후크에 의해 가로채지고, 사용자별 규칙 테이블(permission_rule)과 비교됩니다. 일치하는 규칙이 없으면 작업이 일시 중지되고(상태 waiting_approval), 사용자가 사용하는 채널에서 다음을 요청받습니다:
Telegram: '한 번 허용', '항상 허용', '거부' 버튼이 포함된 메시지.
Web UI: 작업이 확장되면 동일한 세 가지 선택지가 인라인으로 나타나며, 페이지 이동이 필요하지 않습니다.
'항상 허용'은 영구적인 규칙을 작성하여 같은 동작이 다시 요청되는 것을 방지하며, '한 번 허용'/'거부'는 해당 시도에만 적용됩니다. 응답이 없는 프롬프트는 무기한 대기하는 대신 안전 기본값인 거부로 시간 초과(110초) 처리됩니다.
셸(Bash) 명령어는 추가적인 검사를 받습니다: 작은 분류기(app/services/github_classifier.py)가 git
하위 명령어(subcommands)와 범위를 특정 작업에 한정하여 적용합니다 (git status를 읽기 전용으로, git commit은 로컬 변경 사항으로, git push는 게시로 취급하는 대신, 모든 셸 명령을 동일하게 다루지 않도록 합니다. 따라서 '항상 커밋 허용'이 푸시까지 조용히 허용하지 않습니다. Bash를 통해 실행되는 다른 모든 것과 자체 분류기가 없는 도구는 해당 도구당 하나의 거친 버킷으로 폴백(fallback)합니다.
git 외의 다른 연결자(커넥터)로 분류 기능을 확장하는 것은 아직 진행 중이며, 기여를 환영합니다.
Telegram bot ─┐
├── FastAPI backend ── PostgreSQL
Next.js web UI┘ ├── disposable sandboxes (Boat provider)
...
AI 자동 생성 콘텐츠
본 콘텐츠는 GitHub Trending Python (weekly)의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기