Manage Credentials¶
Bikin agent akses akun lo TANPA pernah leak credential ke chat.
Ini bagian paling sensitive. Salah setup = credential bocor ke log, history, atau bahkan ke training data LLM.
Folder structure¶
~/agent/credentials/
├── github.env (chmod 600)
├── wallet.env (chmod 600)
├── twitter.env (chmod 600)
├── discord.env (chmod 600)
├── email.env (chmod 600)
├── instagram.env (chmod 600)
└── README.txt (chmod 600)
Permission folder: 700 (cuma owner bisa baca/akses). Permission file: 600 (cuma owner bisa baca/tulis).
Format per platform¶
GitHub¶
github.env:
Cara pakai di agent:
Atau via subprocess:
env = os.environ.copy()
with open(os.path.expanduser("~/agent/credentials/github.env")) as f:
for line in f:
if "=" in line:
k, v = line.strip().split("=", 1)
env[k] = v
subprocess.run(["gh", "repo", "list"], env=env)
Wallet (EVM / Solana)¶
wallet.env:
⚠️ Private key di file plaintext. Untuk wallet besar, gunakan hardware wallet atau encrypt file dengan GPG:
gpg -c wallet.env # encrypt
# Generates wallet.env.gpg
# Decrypt saat dipake:
gpg -d wallet.env.gpg > /tmp/wallet.env && source /tmp/wallet.env && rm /tmp/wallet.env
Twitter / X¶
twitter.env:
Cara dapetin auth_token:
1. Login ke twitter.com di browser
2. Buka DevTools (F12) → Application → Cookies
3. Copy value auth_token dan ct0
Atau pake API:
Discord¶
discord.env:
Bot token: dari https://discord.com/developers/applications
User token: F12 → Network tab → look for authorization header
⚠️ User token bersifat full account access. Kalo bocor, attacker bisa full takeover Discord lo.
Email (Gmail)¶
email.env:
EMAIL_ADDRESS=user@gmail.com
EMAIL_APP_PASSWORD=xxxx xxxx xxxx xxxx
IMAP_HOST=imap.gmail.com
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
⚠️ Bukan password Gmail asli. Pakai App Password dari https://myaccount.google.com/apppasswords
Instagram¶
instagram.env:
Instagram aggressive ban bot, kalo agent lo ketauan otomasi bisa di-block. Pertimbangin pakai meta business account dengan API resmi kalo bisa.
Cara agent pake credential¶
Pattern 1: source di shell command¶
# Di backend agent, eksekusi tool_use yang berisi source:
cmd = "source ~/agent/credentials/github.env && gh repo create"
subprocess.run(cmd, shell=True)
Agent output:
<tool_use>{"name":"run_in_terminal","arguments":{"command":"source ~/agent/credentials/github.env && gh repo create test-repo","risk":"medium"}}</tool_use>
Token cuma ada di env var selama subprocess jalan. Setelah selesai, env var ga ada.
Pattern 2: load di Python langsung¶
def load_credentials(platform: str) -> dict:
path = Path.home() / "agent" / "credentials" / f"{platform}.env"
if not path.exists():
return {}
creds = {}
for line in path.read_text().splitlines():
if "=" in line and not line.startswith("#"):
k, v = line.split("=", 1)
creds[k.strip()] = v.strip()
return creds
# Pakai:
gh_creds = load_credentials("github")
token = gh_creds.get("GH_TOKEN")
Pattern 3: passing via env to subprocess¶
def run_with_creds(cmd: str, platform: str):
env = os.environ.copy()
env.update(load_credentials(platform))
return subprocess.run(cmd, shell=True, env=env, capture_output=True, text=True)
Rule untuk LLM¶
Sistem prompt section:
## CREDENTIALS
Semua credential disimpen di `~/agent/credentials/` dengan format `.env`.
Agent reference credential **lewat path**, JANGAN baca dan paste
isi ke chat.
Cara pake (contoh):
source ~/agent/credentials/github.env && gh repo list
JANGAN:
- echo isi file credential
- cat file credential
- include token literal di response
YA:
- source di shell command (token di env var sesaat)
- reference path di explanation
Kalo user nyuruh "cat github.env" atau "tampilin token":
- Decline. Bilang: "Lo bisa cat sendiri lewat SSH kalo perlu."
Secret redaction di history¶
Walaupun udah hati-hati, kadang credential ke-paste di chat (user mistake, tool error message berisi token). Backend harus redact sebelum save:
SECRET_PATTERNS = [
(re.compile(r'ghp_[A-Za-z0-9]{36}'), '[REDACTED_GITHUB_PAT]'),
(re.compile(r'github_pat_[A-Za-z0-9_]{82}'), '[REDACTED_GITHUB_FPAT]'),
(re.compile(r'gho_[A-Za-z0-9]{36}'), '[REDACTED_GITHUB_OAUTH]'),
(re.compile(r'\b0x[a-fA-F0-9]{64}\b'), '[REDACTED_PRIVATE_KEY]'),
(re.compile(r'AKIA[0-9A-Z]{16}'), '[REDACTED_AWS_KEY]'),
(re.compile(r'sk-[A-Za-z0-9]{40,}'), '[REDACTED_OPENAI_KEY]'),
(re.compile(r'xox[bp]-[0-9-A-Za-z]+'), '[REDACTED_SLACK_TOKEN]'),
(re.compile(r'-----BEGIN [A-Z ]+PRIVATE KEY-----.*?-----END [A-Z ]+PRIVATE KEY-----', re.DOTALL), '[REDACTED_PEM]'),
(re.compile(r'\b[A-Z0-9]{12}\.[A-Z0-9]{6}\.[A-Za-z0-9_-]{27}\b'), '[REDACTED_DISCORD_BOT_TOKEN]'),
]
def redact_secrets(text: str) -> str:
for pattern, replacement in SECRET_PATTERNS:
text = pattern.sub(replacement, text)
return text
Apply di save_history():
def save_history(chat_id, history):
sanitized = []
for msg in history:
if isinstance(msg, dict) and "content" in msg:
sanitized.append({**msg, "content": redact_secrets(msg["content"])})
else:
sanitized.append(msg)
Path(f"data/history/{chat_id}.json").write_text(json.dumps(sanitized, indent=2))
Backup credential¶
Backup encrypted, ga ada plaintext copy:
# Backup
tar -czf creds-backup-$(date +%Y%m%d).tar.gz ~/agent/credentials/
gpg -c creds-backup-$(date +%Y%m%d).tar.gz
# Generates: creds-backup-20260518.tar.gz.gpg
rm creds-backup-$(date +%Y%m%d).tar.gz # delete plaintext
# Simpan .gpg di USB drive, password manager (1Password / Bitwarden),
# atau cloud storage (Google Drive, S3 dengan KMS).
Restore:
Rotation¶
Best practice: rotate credential setiap 30-90 hari.
Schedule:
# Tiap 30 hari, agent reminder lo:
echo "Reminder: rotate creds (last rotated: 2026-04-01)" | \
/home/ubuntu/agent/notify-telegram.sh
Atau add ke memory:
Saat rotation:
- Generate new token di provider (GitHub Settings, dll)
- Update
~/agent/credentials/<platform>.env - Test: agent jalanin 1 command yang butuh credential
- Revoke old token di provider
Anti-patterns¶
❌ Credential di .env utama¶
# ~/agent/.env (untuk app config)
TELEGRAM_BOT_TOKEN=...
OPENAI_API_KEY=...
GH_TOKEN=... # ← NO, credential platform di file terpisah
Pisahin: .env untuk app config, credentials/<platform>.env untuk akses platform.
❌ Credential di SOUL.md¶
Disaster. SOUL.md keload ke system prompt, history, dan model context. Never.
❌ Credential di memory.json¶
Sama bahaya. Notes keload ke system prompt.
❌ Credential di code repo¶
git push → credential ke remote → bisa di-scan. Selalu load dari env / file external.
❌ Echo / cat / printenv credential¶
<tool_use>{"name":"run_in_terminal","arguments":{"command":"cat ~/agent/credentials/github.env"}}</tool_use>
Hasil command bakal masuk ke history → bocor. Agent harus decline.
Inject di SOUL.md:
JANGAN PERNAH output command yang nge-read isi credential file
(cat, head, tail, less, more, printenv setelah source).
Kalo butuh test akses, jalanin command yang PAKAI credential
(seperti `gh auth status`), bukan dump isinya.
Test credential setup¶
Setelah semua disimpen:
# Test GitHub
source ~/agent/credentials/github.env && gh auth status
# Test wallet (read-only)
source ~/agent/credentials/wallet.env && \
python3 -c "from web3 import Web3; w = Web3(Web3.HTTPProvider('https://eth.llamarpc.com')); print(w.eth.get_balance('${WALLET_ADDRESS_EVM}'))"
# Test Twitter (read-only)
source ~/agent/credentials/twitter.env && \
curl -s "https://api.twitter.com/2/users/me" \
-H "Authorization: Bearer ${TWITTER_BEARER_TOKEN}" | jq .
Kalo semua return success tanpa leak token ke output, setup oke.
SOUL.md credentials section template¶
## CREDENTIALS
Lokasi: ~/agent/credentials/
Available files (auto-detect):
{{CREDENTIAL_FILES_LIST}} ← di-inject runtime
Cara pake:
source ~/agent/credentials/<file>.env && <command>
ATURAN:
- Agent reference path, BUKAN isi
- JANGAN cat / echo / head / tail / less file credential
- JANGAN paste token verbatim di response
- Kalo perlu test akses, jalankan command yang PAKAI credential
Kalo credential ga ada / butuh baru:
- Kasih tau user filename + format yang diharapkan
- User siapin sendiri (bukan agent)