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).
mkdir -p ~/agent/credentials
chmod 700 ~/agent/credentials
chmod 600 ~/agent/credentials/*.env
Format per platform
GitHub
github.env:
GH_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxx
GH_USER=username
Cara pakai di agent:
source ~/agent/credentials/github.env && gh repo list
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:
EVM_PRIVATE_KEY=0x...
SOLANA_PRIVATE_KEY=...
WALLET_ADDRESS_EVM=0x...
WALLET_ADDRESS_SOLANA=...
⚠️ 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:
TWITTER_AUTH_TOKEN=...
TWITTER_CT0=...
TWITTER_USERNAME=...
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:
TWITTER_BEARER_TOKEN=...
TWITTER_API_KEY=...
TWITTER_API_SECRET=...
Discord
discord.env:
DISCORD_BOT_TOKEN=...
DISCORD_USER_TOKEN=...
DISCORD_USER_ID=...
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.env:
IG_USERNAME=...
IG_PASSWORD=...
IG_2FA_SECRET=... (kalo pake 2FA)
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:
gpg -d creds-backup-20260518.tar.gz.gpg | tar -xzf -
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:
/remember credential rotation overdue (last 2026-04-01, 30+ days)
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
## STATE FACTS
- GitHub PAT: ghp_abc...
Disaster. SOUL.md keload ke system prompt, history, dan model context. Never.
❌ Credential di memory.json
{
"notes": [
"gh token: ghp_abc..."
]
}
Sama bahaya. Notes keload ke system prompt.
❌ Credential di code repo
GH_TOKEN = "ghp_abc..."
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)