F-03 Infisical Python integration
Approach
Custom httpx-based client вместо infisicalsdk package:
- Pros: full control над cache/retry/stale logic, минимум deps (только httpx + tenacity + cachetools + structlog), no SDK upgrade hell
- Cons: maintain self, нет automatic API compatibility tracking
API endpoints используем напрямую:
POST /api/v1/auth/universal-auth/login(Machine Identity → access_token)GET /api/v3/secrets/raw/{secretName}?workspaceId=...&environment=...&secretPath=...
Architecture
packages/core/src/razmakh_core/secrets/:
exceptions.py— InfisicalError hierarchyinfisical_client.py— main client classsecret_ref.py— URI parserrazmakh://env/path/KEY__init__.py— public API exports
Cache + stale-while-error pattern
Two-level TTL:
- TTL=120s: fresh cache hit, no network call
- TTL=120s..720s (10 min stale window): returns stale + WARN log + tries fresh fetch on each call
- TTL>720s: cache evicted, fail-fast
Это balance:
- 120s fresh = secrets propagation lag после rotation (acceptable для P22 runtime config)
- 10 min stale = enough для short Infisical outages (restart, network blip)
- Beyond 10 min — assume permanently broken, app should know
Test coverage (26 tests)
- Config: missing env vars, empty creds, full setup
- Auth: 401 fail, token caching
- Get secret: success, cache hit, 404, force_refresh bypass
- Stale-while-error: stale returned on connection error, too old raises, no cache raises
- Retry: 5xx → retry → success
- Cache invalidation: all + per-project
Все 26 ✅ + integration smoke test против real Infisical (https://secrets.razmakh.ru) — cfut_7lYQmQQ... retrieved + cached + via secret_ref.
Lint adjustments
ruff strict catches false positives в tests:
S105/S106/S107(hardcoded password) — fixture values “csec”, “cid”RUF001/002/003(ambiguous unicode) — кириллица в comments / strings
Fix: [tool.ruff.lint.per-file-ignores]:
"**/tests/**/*.py" = ["S101", "S105", "S106", "S107"]"**/*.py" = ["RUF001", "RUF002", "RUF003"]Глобальный RUF001-003 ignore = intentional. Razmakh русскоязычный проект, кириллица в comments / docstrings нормальна, не security issue.
What this enables
- F-04 SQLAlchemy + Alembic может resolved DB password из Infisical (
razmakh://prod/_internal/postgres/PASSWORD) - F-08 FastAPI startup hook читает secrets через
resolve_secret_ref()для:- WB API tokens per organization (Phase 1.5+)
- Cloudflare token (для Caddy admin API если нужно)
- LLM API keys (DeepSeek / GigaChat)
- Backend config файл хранит
secret_ref://...URIs вместо real values - GitHub Actions deploy не нужно знать ни одного real secret кроме INFISICAL_CLIENT_*
Deferred (Phase 1.5+)
- Async variant
AsyncInfisicalClient(когда FastAPI startup async deps) - Per-organization client factory (Block 1.5.1 — multi-org RBAC)
- Cross-tenant probe в CI (Block 1.5.5)
- Chaos test: Infisical 5 min down (Block 1.5.5)
- Auto-rotation webhook → cache invalidate
ID metric
- Implementation time: ~50 min
- Test development: ~30 min (включая фиксы pytest respx context manager pattern)
- Docs: ~15 min
- Integration smoke test against real Infisical: 2 min
- Total: ~1.5h vs 2-3 дня в plan estimate (~10x faster через LLM)