Files

6.2 KiB

name, description, version, author, license, metadata
name description version author license metadata
credential-management Centralized credential management for 9Webs automation scripts — single .env file, Python loader, migration pattern. No more hardcoded passwords in scripts. 1.0.0 Hermes Agent MIT
hermes
tags related_skills
credentials
security
secrets
env
dotenv
miaoshou-erp
atomk-platform
tongtool-workflows

Credential Management

Centralized credential infrastructure for all 9Webs automation scripts. Scripts read credentials from a single ~/.hermes/credentials.env file via a Python loader module — no hardcoded passwords anywhere.

When to use

  • Writing a new automation script that needs AtomK, Ozon, Gitea, Miaoshou, or email credentials
  • Migrating an existing hardcoded-credential script to the centralized pattern
  • Rotating credentials across all scripts at once
  • Debugging a KeyError from atomk_credentials.get_credential()

Architecture

~/.hermes/
├── credentials.env          # Single source of truth (chmod 600, never committed)
└── scripts/
    └── atomk_credentials.py # Python loader with typed helpers

credentials.env format

ATOMLISTING_USERNAME=admincao
ATOMLISTING_PASSWORD=actual-value-here
ATOMLISTING_API_BASE=https://www.atomlisting.com
MIAOSHOU_USERNAME=...
MIAOSHOU_PASSWORD=...
OZON1_EMAIL=...
OZON1_PASSWORD=...
GITEA_USERNAME=admin9webs
GITEA_PASSWORD=...

Rules:

  • chmod 600 — owner read/write only
  • Never committed to Git (add to .gitignore)
  • FILL_ME placeholder means the value is not yet set
  • Override path with ATOMK_CREDENTIALS_FILE env var

Python loader API

import sys, os
sys.path.insert(0, os.path.expanduser("~/.hermes/scripts"))
from atomk_credentials import (
    get_credential,       # key → value
    get_atomk_auth,       # → (username, password)
    get_miaoshou_auth,    # → (username, password)
    get_ozon_auth,        # → (email, password)  — store=1 or 2
    get_gitea_auth,       # → (username, password)
)

The loader caches on first access. get_credential() raises KeyError if the value is unset or still FILL_ME.

Migration pattern

When moving a script from hardcoded credentials to the loader:

Before (BAD):

ATOMK_USERNAME = "admincao"
ATOMK_PASSWORD = "Tt123456!"

After (GOOD):

import sys, os
sys.path.insert(0, os.path.expanduser("~/.hermes/scripts"))
from atomk_credentials import get_atomk_auth

ATOMK_USERNAME, ATOMK_PASSWORD = get_atomk_auth()

Steps for each script:

  1. Add import sys, os if not present
  2. Add the sys.path.insert line before the credential import
  3. Import the appropriate helper function
  4. Replace hardcoded values with the function call
  5. Run python3 -c "import ast; ast.parse(open('script.py').read())" to verify syntax
  6. Test the script with the credential file populated

Adding new credentials

  1. Add the variable to ~/.hermes/credentials.env with an initial FILL_ME placeholder
  2. Add a typed helper function to atomk_credentials.py (e.g., get_new_service_auth())
  3. Fill in the actual value in credentials.env

Pitfalls

  1. KeyError on first run — the credential file ships with FILL_ME placeholders. The user must replace them with real values.
  2. ModuleNotFoundError: atomk_credentials — the script doesn't have sys.path.insert(0, ...) before the import. The scripts/ directory is not on Python's default path.
  3. Syntax error after migration — verify with ast.parse() as shown above. The refactored lines must be valid Python.
  4. Python 3.12+ f-string backslash restriction — if the script uses backslash-escaped quotes inside f-strings (e.g., f'{\"embedded\"}'), extract the replacement string to a variable first. This is a Python version constraint, not credential-specific.
  5. Credentials on command line — never pass credentials as command-line arguments. They are visible in ps output and shell history. Always read from env or file.
  6. MongoDB URI encoding with special chars — passwords containing @ (%40) and # (%23) can fail authentication when passed via URI string, even after quote_plus(). Workaround: pass username= and password= directly to MongoClient() constructor parameters instead of encoding them into the URI. See references/mongodb-auth-patterns.md for detailed examples.
  7. Python venv/PEP 668 — this host enforces externally-managed Python (--system flag rejected). Use python3 -m venv /tmp/venv && /tmp/venv/bin/pip install ... for one-off tool deps (e.g., pymongo). Do not waste time fighting uv pip install --system.
  • Tencent Cloud / tccli: ~/.tccli/default.credential (INI format). Separate from ~/.hermes/credentials.env. Used by tencent-cloud-operations skill for TAT, Lighthouse, and CVM API access. Managed independently.
  • Production server .env: /root/AtomK_Operation_Tools/.env on SG3 (43.134.190.229). Contains MongoDB, MySQL, API keys, and AI service credentials for the running AtomK_Server backend.
  • premiumproducts MongoDB: mongodb://root:***@43.134.190.229:27018/premiumproducts?authSource=admin (password contains @ and #). New database (2026-07) for premium product management. Use direct constructor params — not URI encoding. See references/mongodb-auth-patterns.md.

Security rules

  1. Never print() or log() a credential value
  2. Never pass credentials on the command line (visible in ps)
  3. Never commit credentials.env to Git
  4. After rotating a password, update credentials.env — no script-level changes needed
  5. The loader checks for FILL_ME and raises KeyError rather than silently using a placeholder
  6. TAT (Tencent Automation Tools) aggressively masks passwords in remote command output — use xxd hex dump or char-by-char ord() extraction to read credentials from remote .env files

Support files

  • templates/credentials.env — clean template to copy to ~/.hermes/credentials.env
  • references/audit-findings.md — 2026-06-22 security audit credential exposure summary
  • scripts/scan_hardcoded_secrets.py — scan a directory for known hardcoded secrets and suspicious patterns