chamelion-vault
자체 호스팅 chamelion vault 게이트웨이에서 시크릿 값을 받아 오는 클라이언트입니다.
- 의존성이 없습니다. 표준 라이브러리만 씁니다.
- 값을 디스크에 쓰지 않고 캐시하지 않습니다. 받은 값은 프로세스 메모리에만 있습니다.
- 로깅을 설정하지 않습니다. 이 패키지는 여러분의 애플리케이션 안에서 돕니다.
이 패키지는 볼트를 운영하는 조직의 구성원을 위한 것입니다. 볼트 주소·API 키· 터널 접속 정보는 여기 없고 사내 문서에 있습니다. 그것을 모르면 설치까지는 되지만 값을 받는 단계에서 막히는 것이 정상입니다.
쓰는 법
범위(팀 · 프로젝트 · 환경 · 앱)를 정한 Vault 를 하나 만들고, 그 위에서 받습니다.
from chamelion_vault import Vault
vault = Vault(team="myteam", project="myproject", env="dev", app="api")
api_key = vault.secret("EXAMPLE_KEY").get_secret_value() # 값 하나
everything = vault.secrets() # 받을 수 있는 것 전부
| 기능 | 하는 일 | 돌려주는 것 |
|---|---|---|
Vault(team=, project=, env=, app=) |
범위를 정합니다. 비운 칸은 환경변수에서 읽습니다 | Vault |
vault.secret(name) |
시크릿 하나의 값 | 값 상자(SecretStr) |
vault.secret(name, field=) |
번들의 필드 하나 | 값 상자 |
vault.secrets(shared=False) |
이 범위에서 받을 수 있는 것 전부 | 이름 → Secret |
chamelion-vault run -- 명령 |
전부 받아 명령의 환경변수로 | 명령의 종료 코드 |
같은 코드가 노트북에서는 dev, 배포 서버에서는 prod 를 받도록 범위를 환경변수로 둘 수 있습니다.
export CHAMELION_VAULT_TEAM=myteam CHAMELION_VAULT_PROJECT=myproject CHAMELION_VAULT_ENV=dev
vault = Vault() # 팀 · 프로젝트 · 환경 · 앱을 환경변수에서
범위가 비어 있으면 요청을 보내기 전에 MissingScopeError 가 비어 있는 칸을 전부 알려 줍니다.
앱
한 프로젝트 안의 서비스마다 다른 값(각자의 DATABASE_URL 등)은 앱 전용으로 등록됩니다.
app 을 주면 프로젝트 공통 + 그 앱 전용을 받고, 다른 앱의 값은 오지 않습니다 —
secret() 도 그 앱에 없는 이름은 프로젝트 공통에서 찾습니다. CHAMELION_VAULT_APP 이 설정된 환경에서
공통만 받으려면 Vault(app="") · --app "" 를 씁니다. 공통과 앱
전용에 같은 이름은 등록할 수 없으므로 어느 값이 이기는지 고민할 일이 없습니다.
번들 — 필드가 여럿인 시크릿
아이디 · 비밀번호처럼 한 몸인 크리덴셜은 한 시크릿 안의 여러 필드입니다. secret() 은
어느 필드인지 추측하지 않습니다.
vault.secret("EXAMPLE_ACCOUNT", field="password").get_secret_value()
vault.secret("EXAMPLE_ACCOUNT") # FieldSelectionError — 필드 이름(password, username)만 알려 줍니다
전부 필요하면 secrets() 의 Secret 을 씁니다.
account = vault.secrets()["EXAMPLE_ACCOUNT"]
sorted(account) # ['password', 'username']
account["username"].get_secret_value()
account.project, account.env, account.app, account.name # 위치 — 비밀이 아닙니다
Secret.path 는 0.2 호환용 표기입니다. 공용 · common 에서 온 값의 실제 위치는 project · env 로 확인하세요.
공용 값까지 한 번에
여러 프로젝트가 함께 쓰는 값은 볼트의 공용 프로젝트에 있습니다. 볼트 관리자가 내 프로젝트에 그 공용 프로젝트를 구독으로 열어 두면 한 번에 받습니다.
everything = vault.secrets(shared=True)
shared=True 는 프로젝트 공통 + 앱 전용 + 환경과 무관한 값(common) + 구독한 공용
프로젝트(같은 환경과 common)를 한 응답으로 받습니다. 두 곳에 같은 이름이 있으면 서버가
어느 쪽도 고르지 않고 전부 거부합니다.
.env 파일 없이 실행하기
chamelion-vault run -- python app.py
chamelion-vault run --app api --shared -- python app.py
받을 수 있는 값을 전부 받아 실행할 명령의 환경변수로만 넣습니다. 파일을 만들지 않고,
값을 화면에 찍지 않습니다. 이름은 시크릿 이름 그대로이고, 번들은 이름_필드 입니다
(EXAMPLE_DB_HOST). 이미 있는 환경변수와 이름이 겹치면 볼트 값이 덮어쓰고, 덮인 이름을
경고로 알립니다. 실행한 명령에는 API 키(CHAMELION_VAULT_KEY)를 넘기지 않습니다.
값이 상자에 담겨 옵니다
get_secret_value() 로 열기 전까지 값은 어디에도 그대로 나타나지 않습니다.
value = vault.secret("EXAMPLE_KEY")
print(value) # <REDACTED>
print(vault) # Vault(myproject/dev/api) — 키는 객체에 없습니다
무심코 남긴 로그 한 줄에 진짜 키가 찍히는 것을 막습니다. 연 뒤에는 막아 주지 않으니 꺼낸 값은 바로 쓰고 변수에 오래 들고 있지 마세요. 이 패키지는 값을 캐시하지 않습니다 — 같은 값을 두 번 부르면 두 번 요청합니다.
키는 두 종류입니다
| 키 | 받는 곳 | 열리는 것 |
|---|---|---|
| 개인 액세스 키 | 웹 콘솔 계정 메뉴 · Slack /vault key — 승인 없음 |
내가 멤버인 프로젝트의 local · dev · staging · common |
| 서비스 키 | 볼트 관리자가 발급 | 그 프로젝트의 모든 환경(prod 포함) · 구독한 공용 프로젝트 |
- 개인 액세스 키로는
prod를 받지 못합니다(본인 비공개 시크릿도 마찬가지). 노트북의 키가 새도 운영 서비스가 무사하도록 정한 규칙입니다.prod는 배포 서버 · CI 에 둔 서비스 키로 받습니다. - 개인 액세스 키는 30일 동안 쓰지 않으면 만료됩니다. 다시 받으면 이전 키는 그 자리에서 폐기됩니다 — 잃어버렸을 때도 다시 받으면 됩니다.
- 키는 Slack 창에 한 번만 표시됩니다.
처음 한 번 — 빈 프로젝트에서 값이 올 때까지
네 단계입니다. 어느 하나가 빠져도 값이 오지 않고, 볼트는 어느 단계가 빠졌는지 알려주지 않습니다 — 대신 이 패키지의 오류 문안이 다음에 할 일을 알려줍니다.
① 설치
mkdir myproject && cd myproject
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install chamelion-vault
pip list 에 chamelion-vault 하나만 늘어납니다. 딸려 오는 것이 없습니다.
② 키 — 개인 액세스 키를 받아 환경변수에 넣습니다.
read -rs CHAMELION_VAULT_KEY && export CHAMELION_VAULT_KEY
⚠️ 키를 명령줄에 그대로 쓰지 마세요. 셸 기록에 원문이 남습니다. 위 명령은 입력이 화면에 보이지 않는 것이 정상입니다.
③ 터널 — 창을 하나 따로 열어 띄웁니다. 아래 "터널" 절을 보세요.
ssh -N vault-tunnel
④ 받기
# app.py
from chamelion_vault import Vault, VaultError
try:
value = Vault(team="myteam", project="myproject", env="dev").secret("EXAMPLE_KEY")
except VaultError as error:
raise SystemExit(f"\n{error}")
print(value) # <REDACTED>
<REDACTED> 가 찍히면 값이 온 것입니다. 값 자체는 찍히지 않습니다.
설정 — 환경변수
| 환경변수 | 뜻 | 기본값 |
|---|---|---|
CHAMELION_VAULT_KEY |
API 키 (개인 액세스 · 서비스) | 없음 (필수) |
CHAMELION_VAULT_URL |
게이트웨이 주소 | http://127.0.0.1:8081 |
CHAMELION_VAULT_TEAM |
Vault 의 팀 |
없음 |
CHAMELION_VAULT_PROJECT |
Vault 의 프로젝트 |
없음 |
CHAMELION_VAULT_ENV |
Vault 의 환경 |
없음 |
CHAMELION_VAULT_APP |
Vault 의 앱 |
없음 — 프로젝트 공통만 |
설정 파일은 지원하지 않습니다. 파일을 읽게 만들면 거기에 키를 적게 되고, 그 파일은 언젠가 저장소에 들어갑니다. 키를 인자로 받지도 않습니다 — 같은 이유입니다.
터널
게이트웨이는 볼트 호스트의 루프백에만 열려 있습니다. 밖에서 값을 받으려면 그 루프백까지 가는 SSH 터널이 필요하고, 이 패키지는 터널을 대신 띄우지 않습니다. 프로세스 수명·키 경로·OS 차이가 전부 따라 들어와, 실패했을 때 터널 문제인지 조회 문제인지 구분이 안 되기 때문입니다.
~/.ssh/config 에 한 번 적어 두면 그 뒤로는 한 줄입니다.
Host vault-tunnel
HostName <볼트 호스트>
Port <SSH 포트>
User <계정>
IdentityFile ~/.ssh/<터널 키>
IdentitiesOnly yes
LocalForward 8081 127.0.0.1:8081
ssh -N vault-tunnel
⚠️ 왼쪽 포트와 오른쪽 포트는 다른 것입니다.
LocalForward 8081 127.0.0.1:8081에서 앞의8081은 내 컴퓨터에서 고른 포트이고, 뒤의127.0.0.1:8081은 볼트 호스트의 게이트웨이입니다. 내 컴퓨터에서 8081 을 이미 쓰고 있다면 앞의 값만 바꾸고CHAMELION_VAULT_URL을 거기에 맞추세요. 뒤의 값을 바꾸면 터널이 아예 열리지 않습니다.LocalForward 8082 127.0.0.1:8081export CHAMELION_VAULT_URL=http://127.0.0.1:8082
⚠️
IdentitiesOnly yes를 빠뜨리지 마세요. 이것이 없으면 기본 키(~/.ssh/id_*)가 먼저 인증을 시도해, 터널 키에 걸어 둔 제약이 통째로 비껴갑니다. 실패 원인을 찾기 가장 어려운 자리입니다.
<볼트 호스트>·<SSH 포트>·<계정>·<터널 키> 는 사내 터널 문서에 있습니다.
값을 받지 못했을 때
볼트는 실패 원인을 알려주지 않습니다. 키가 틀렸는지 · 권한이 없는지 · 그런 시크릿이 없는지를 응답으로 가르면 그것이 곧 남의 볼트를 훑는 도구가 되기 때문입니다. 그래서 이 패키지도 추측하지 않고, 대신 확실히 아는 것만 말합니다.
| 예외 | 뜻 | 할 일 |
|---|---|---|
MissingApiKeyError |
키 환경변수가 비었다 | 개인 액세스 키를 받는다 |
MissingScopeError |
팀 · 프로젝트 · 환경 중 빈 칸이 있다 | 인자나 환경변수를 채운다 |
VaultUnreachableError |
연결 자체가 안 됐다 | 터널이 떠 있는지 본다 |
AccessRefusedError |
볼트가 값을 주지 않았다 | 메시지의 원인 순서대로 확인한다 |
FieldSelectionError |
번들에서 필드를 고르지 않았다 | field= 로 고른다 |
UnexpectedResponseError |
계약에 없는 응답이 왔다 | 주소가 정말 게이트웨이인지 확인한다 |
AccessRefusedError 의 흔한 원인 순서는 멤버십 → 환경(개인 액세스 키로 prod) → 팀 이름 →
키 만료 → 이름 오타 → 공용 겹침 → 남의 비공개 시크릿 입니다. 모든 예외는 VaultError 를 상속하므로 한 번에
잡을 수 있습니다.
받을 수 없는 것
- 남의 비공개 시크릿. 소유자 본인만 받습니다. 관리자도 값을 볼 수 없습니다. 여러 사람 · 서버가 써야 하는 값이라면 비공개로 두면 안 됩니다.
- 다른 앱 · 다른 프로젝트의 값. 범위 밖의 값은 응답에 들어가지 않고, 무엇이 빠졌는지도 알려주지 않습니다. 꼭 있어야 하는 이름이 있으면 받은 뒤 직접 확인하세요.
0.1 방식에서 옮기기
경로로 받던 get_secret · get_secrets 는 0.3.0 에서 제거 예정 경고를 내고 그대로 동작합니다.
# 0.1
from chamelion_vault import get_secret
get_secret("myproject", "dev", "EXAMPLE_KEY")["value"]
# 0.3
from chamelion_vault import Vault
Vault(team="myteam", project="myproject", env="dev").secret("EXAMPLE_KEY")
0.1 의 머신 키는 Vault 로 받지 못합니다. 개인 액세스 키나 서비스 키로 바꿔 주세요.
Metadata
Release files for chamelion-vault 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| chamelion_vault-0.3.1.tar.gz | 17.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| chamelion_vault-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 39.5 kB
Release files / chamelion_vault-0.3.1.tar.gz
| Download URL | chamelion_vault-0.3.1.tar.gz |
|---|---|
| Size | 17.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b69cbbf6b1b315db0e8d9fb0acc7b62483342ce0b88ac88d23326c18605682da
|
|
BLAKE2b-256 checksum How to use checksums |
07043166116956a802a1cc068ff162fbd32ec0aeb0de0a403ef2b199f6145639
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency logRelease files / chamelion_vault-0.3.1-py3-none-any.whl
| Download URL | chamelion_vault-0.3.1-py3-none-any.whl |
|---|---|
| Size | 22.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
715b3dcb543407ed17aa7eddcb9452b3e065e8668d7c23c5505b3321f03ba7a0
|
|
BLAKE2b-256 checksum How to use checksums |
fa6183164515b3537044be61c80c09577ee613254717e6502b0a69c56352d746
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency log