별도 제작 프로젝트
Kavranta
기존 환경 변수 파일의 편집·연결·암호화 공유·배포를 한곳에서 관리하고, AI에는 비밀값 대신 제한된 작업 권한을 제공하는 로컬 우선 데스크톱 앱.
- 플랫폼
- macOS · Windows · Tauri · React · Rust
- 역할
- Product · Desktop development · Security design
- 기간
- 2026.08 — 개발 중
만들게 된 계기
GitHub Actions 자동 배포 파이프라인을 만들면서 이 문제를 더 선명하게 느꼈습니다. 로컬 환경 변수 파일에 값을 적고, GitHub Secrets에도 다시 등록한 뒤, Cloudflare나 AWS 같은 배포 대상에도 같은 값을 별도로 넣어야 했습니다. 값 하나가 바뀔 때마다 여러 화면을 오가며 복사하는 흐름은 번거롭고, 어느 곳이 최신인지 놓치기 쉬웠습니다.
그래서 한 프로젝트의 변수 구조와 적용 대상을 한 화면에서 정리하고, 필요한 값만 선택해 배포 인프라에 적용할 수 있으면 좋겠다고 생각했습니다. 동시에 AI 에이전트와 작업할 때는 필요한 구조를 보여 주고 싶어도 비밀값까지 노출될 수 있다는 점이 불편했습니다.
Kavranta는 이미 프로젝트가 사용하는 환경 변수 파일을 바꾸지 않고, 그 위에서 구조를 정리하고 안전하게 수정하기 위해 만든 로컬 우선 데스크톱 앱입니다. 비밀값을 새 저장소로 옮기는 vault가 아니라, 기존 파일을 더 명확하게 다루고 필요한 배포 대상에만 적용할 수 있게 돕는 제어 화면을 목표로 했습니다.
처음에는 환경 변수 편집과 연결에 집중했던 Env Manager를, 팀 전달과 배포 후 확인까지 이어지는 Kavranta로 발전시켰습니다. 기능을 넓히면서도 사용자가 이미 쓰는 파일과 실행 방식을 유지하고, AI가 작업하는 데 필요한 권한을 좁게 나누는 기준을 지켰습니다.
기존 파일을 그대로 보존하는 편집기
앱은 사용자가 등록한 프로젝트 안에서만 환경 변수 파일을 찾습니다. 값은 원래 위치에 남고, 편집도 실제 파일에 반영됩니다. 이때 주석, 변수 순서, 따옴표 스타일, 줄바꿈처럼 개발자가 이미 쌓아 둔 형식을 함부로 바꾸지 않는 것을 중요한 요구사항으로 뒀습니다.
같은 이름의 변수가 여러 파일에 있다고 자동으로 같은 값이라고 가정하지도 않습니다. 각각이 독립 값인지, 런타임 오버라이드인지, 의도적으로 연결된 값인지를 구분하고, 사용자가 명시적으로 연결한 경우에만 여러 파일을 함께 바꿉니다.
프로젝트 개요에서는 미입력 값, AI 접근 정책 검토, 파싱 경고와 Git 위험을 함께 확인합니다. 편집기에서는 값이 없는 변수만 모아 볼 수 있도록 했습니다. 처음 실행할 때 수십 개의 설정을 훑는 대신, 아직 채워야 할 항목부터 처리할 수 있게 만든 기능입니다.
AI 에이전트와 값의 경계를 나누기
이 프로젝트에서 가장 오래 고민한 부분은 AI 연동이었습니다. AI가 환경 변수 구조를 정리하는 데 도움을 줄 수는 있지만, 일반적인 파일 읽기 권한만으로 비밀값 전체를 전달하는 방식은 위험합니다.
그래서 Rust 코어를 탐색, 파싱, 정책 검사, 연결 검증, 파일 변경의 유일한 권한으로 두고, AI 에이전트에는 로컬 broker를 통해 좁은 기능만 제공합니다. 기본적으로 에이전트는 변수 이름, 파일 경로, 값 존재 여부와 관계 정보만 보고, protected 값은 읽을 수 없습니다. read-write로 명시적으로 분류된 값도 전용 기능을 요청할 때만 제한적으로 다룹니다.
Codex, Claude Code, GitHub Copilot/VS Code, Cursor가 같은 broker 정책을 사용하도록 연동했습니다. 일반 구조 조회에서는 read-write로 분류된 항목도 값을 반환하지 않고, 실제 값이 필요한 작업은 별도 기능으로 구분했습니다.
안전한 변경을 제품 기능으로 만들기
환경 변수 파일 수정은 작은 실수로도 실행 환경을 망가뜨릴 수 있습니다. 그래서 변경은 바로 덮어쓰지 않고, 현재 파일 상태 확인, 변경 계획, 재파싱과 검증, 충돌 확인을 거치는 흐름으로 설계했습니다.
여러 파일에 연결된 값을 저장할 때는 어떤 파일이 함께 바뀌는지 보여 주고, 외부 변경과 충돌하면 사용자가 판단할 수 있게 합니다. 이 과정에서 React는 값이 아닌 좁은 화면 모델만 받고, 보호된 값은 일반 프론트엔드 상태에 오래 남지 않도록 경계를 나눴습니다.
배포 환경으로 보낼 때도 사용자가 결정하게 하기
필요한 변수만 GitHub Actions, Cloudflare Workers, Expo EAS, AWS Secrets Manager와 SSM Parameter Store로 보낼 수 있습니다. 프로젝트 설정과 기존 로그인 정보를 바탕으로 대상 계정·프로젝트·환경을 먼저 확인하고, 선택한 변수와 적용 범위를 보여 줍니다. Expo EAS에서는 개발·프리뷰·프로덕션 환경을 선택해 전달할 수 있습니다.
대상 서비스에 따라 전달 방법은 달라도, 비밀값을 명령행 인자나 임시 파일에 남기지 않는 기준은 같습니다. 내장 연동 외에는 사용자가 신뢰하는 로컬 CLI를 Personal Provider Pack으로 설치할 수 있고, 이 경우에는 허용된 실행 파일에 표준 입력으로만 값을 전달합니다.
데스크톱과 지원되는 AI 에이전트는 같은 배포 엔진을 사용합니다. AI를 통한 요청도 원본 파일, 정확한 변수 이름, 대상과 정책을 확인하는 과정을 거칩니다. 배포는 명시적으로 요청한 단방향 작업이며, 선택하지 않은 원격 항목을 지우지 않습니다.
배포 성공과 실제 설정 일치를 구분하기
배포 요청이 성공했다는 사실만으로 현재 설정이 로컬과 같은지는 알 수 없습니다. 그래서 AWS와 등록된 서버 실행 환경에는 별도의 비교 기능을 만들었습니다. 화면과 AI에는 원격 값이나 해시 대신 같음·다름·미설정 상태만 돌려줍니다.
서버 비교는 관리자가 고정된 검증 프로그램을 설치하고, 허용할 대상과 변수 이름을 지정한 경우에만 실행합니다. SSH를 통해 암호화된 비교 요청을 전달하며, 이 기능이 서버 파일을 수정하지는 않습니다. GitHub와 Cloudflare의 비밀값은 다시 읽을 수 없으므로 동일한 값 비교를 제공한다고 표시하지 않습니다. 사용자가 확인할 수 있는 범위를 정확히 보여 주는 것도 배포 경험의 일부라고 봤습니다.
팀에 필요한 설정만 암호화해 전달하기
프로젝트 전체를 넘겨야 할 때도 있지만, 동료에게 몇 개의 변수만 필요한 경우도 있습니다. 그래서 파일과 변수 단위로 전달 범위를 고르고, 연결된 항목은 함께 선택할 수 있게 했습니다. 암호화 내보내기 과정에서는 중간 평문 ZIP을 디스크에 만들지 않습니다.
팀이 이미 쓰는 NAS나 동기화 폴더에는 Folder Team Channels를 연결할 수 있습니다. 해당 폴더에는 암호화된 패키지를 새 버전으로 쌓고, 팀원은 필요한 패키지를 가져와 적용합니다. 기존 폴더의 접근 권한을 활용하면서, 전달 기록을 덮어쓰지 않는 방식입니다.
받는 쪽에서는 대상 파일과 충돌을 먼저 확인합니다. 로컬 값을 유지하는 선택을 기본으로 두고, 수신자에게만 있는 내용은 보존하며, 서로 다른 값은 항목별로 판단하게 했습니다. 이미 연결된 변수 그룹은 하나의 선택으로 처리합니다. 팀 공유에서 중요했던 것은 자동으로 같게 만드는 속도보다, 각자의 환경을 실수 없이 이어받는 과정이었습니다.
비밀값을 보지 않고 생성하고 사용하는 작업
AI가 새 비밀값을 설정하려면 생성 결과를 대화로 받아 다시 파일에 적는 흐름이 생기기 쉽습니다. Kavranta에서는 유효 시간이 있는 일회성 저장 계획을 만들고, 신뢰하는 로컬 생성기의 출력을 표준 입력으로 broker에 직접 전달할 수 있게 했습니다. 요청한 값은 파일에 저장되지만, 정상 작업 흐름에서 생성 결과를 AI 응답으로 돌려주지 않습니다.
Action Packs는 관리 중인 값을 사용하는 작업으로 범위를 넓힙니다. 로컬 CLI 작업이나 고정된 HTTPS 확인 요청을 미리 선언하고, 지정된 값 하나를 내부에서 전달합니다. 값과 응답 본문을 일반 UI나 AI 대화에 반환하지 않는 방식입니다. 배포, 생성, 확인이 서로 다른 기능이어도 “이 작업에 필요한 값만 정해진 경로로 사용한다”는 기준으로 설계했습니다.
여러 프로젝트와 Git 위험을 함께 살피기
같은 이름의 변수를 다른 등록 프로젝트에서 찾아, 사용자가 명시적으로 선택했을 때만 현재 프로젝트로 한 번 복사할 수 있습니다. 이때도 AI나 일반 화면에는 값 대신 존재 여부와 파일·변수 이름, 적용 범위만 보입니다. 복사가 끝나면 두 값은 다시 독립적으로 관리됩니다. 편리한 복사를 영구 동기화 관계로 바꾸지 않는 이유입니다.
Git과의 관계도 함께 확인합니다. 관리 중인 파일이 .gitignore에 의해 실제로 무시되는지, 이미 Git에 추적되고 있는지를 구분해 보여 줍니다. 필요한 경우 사용자가 정확한 ignore 규칙을 추가할 수 있지만, Kavranta가 스테이징·커밋·푸시·히스토리 변경을 대신 실행하지는 않습니다. 이미 추적된 비밀값은 별도의 저장소 정리 문제가 남는다는 점도 숨기지 않습니다.
AI 도구를 연결하되, 값 대신 작업 권한을 준다
지원하는 AI 도구는 공통 broker와 Skill을 통해 변수 구조, 분류, 관계, 변경 계획과 실행 결과를 다룹니다. 보호된 값과 아직 분류하지 않은 값은 broker의 명시적 값 조회에서도 차단합니다.
앱에서는 도구가 설치되어 있는지, 어떤 버전의 연동 번들이 연결되어 있는지, 보호 계층이 무엇인지 확인하고 설치·업데이트할 수 있습니다. 작업 기록도 값 없이 남깁니다. 어떤 도구가 어느 프로젝트에서 구조를 확인했는지, 어떤 계획과 변경을 실행했는지를 살필 수 있어 AI가 허용된 범위 안에서 일했는지 추적할 수 있습니다.
Cursor에서는 설치된 설정과 실제 플러그인 활성화를 구분해 안내합니다. 도구별 정책과 설정에 따라 보호 수준이 달라지므로, 설치됐다는 사실만으로 모든 접근이 차단됐다고 표시하지 않습니다.
다만 이 기능을 과장하지 않습니다. Skill의 지침이나 host hook만으로 운영체제 수준의 격리가 완성되는 것은 아닙니다. Kavranta가 약속하는 것은 원본 값 대신 좁은 broker 작업 권한을 제공하고, 지원되는 도구에서 직접 접근을 줄이는 것입니다.
반복해서 쓰는 계정 정보도 프로젝트별로 허용하기
현재 개발 브랜치에는 반복해서 사용하는 계정 정보를 macOS Keychain이나 Windows Credential Manager에 보관하는 선택 기능도 추가했습니다. 앱의 로컬 데이터에는 계정 이름과 프로젝트별 허용 관계를 남기고, 새 계정은 모든 프로젝트에서 접근이 막힌 상태로 시작합니다.
허용된 프로젝트에서도 사용자의 직접 조작으로 필요한 항목을 복사하며, 복사한 내용이 바뀌지 않았으면 45초 뒤 클립보드를 비웁니다. 프로젝트에 계정을 허용하는 것과 AI에 자격 증명 조회나 로그인 실행 권한을 주는 것은 별개의 결정으로 두었습니다.
개발·사업적으로 남은 판단
보안 기능은 “암호화”보다 데이터 흐름을 먼저 설계해야 한다
값을 별도 vault에 넣는 방식이 더 안전해 보일 수 있지만, 사용자는 이미 자신의 런타임이 읽는 파일을 가지고 있습니다. Kavranta는 그 흐름을 바꾸는 대신, 값이 어디를 통과하고 어느 화면에 남는지를 줄이는 쪽을 선택했습니다. 제품의 보안 약속은 화려한 표현보다 명확한 경계에서 나온다는 것을 배웠습니다.
AI 연동의 단위는 파일 접근이 아니라 작업 권한이어야 한다
“이 파일을 읽어도 된다”는 권한은 환경 변수 파일에서 너무 넓습니다. 반대로 “이 프로젝트의 구조를 보고, 빈 변수를 만들고, 명시적으로 허용된 연결만 수정할 수 있다”는 권한은 제품의 목적과 맞습니다. AI를 붙일 때는 모델 성능보다 먼저 어떤 행동을 허용할지 모델링해야 했습니다.
신뢰는 기존 워크플로를 대체하지 않을 때 더 빨리 쌓인다
새 명령어, 새 vault, 새 실행 환경을 강요하면 환경 변수 관리 도구가 오히려 팀의 부담이 됩니다. 기존 개발·배포 명령을 유지한 채 정리와 안전장치만 더하는 방향이 도입 장벽을 낮췄습니다. 개발자 도구는 새로운 습관을 만들기보다 기존 습관의 위험을 줄일 때 더 오래 사용된다고 생각합니다.
자동화가 늘수록 결과를 확인할 수 있는 범위를 정확히 보여 줘야 한다
로컬 파일 저장, 원격 배포 성공, 실제 값 일치는 각각 다른 결과입니다. 이들을 하나의 성공 표시로 합치면 사용자는 어디까지 검증됐는지 알 수 없습니다. 값 자체를 보여 주지 않으면서도 확인 상태와 충돌, 적용 범위를 전달하는 일이 이 도구의 중요한 설계 과제가 됐습니다.


