Coverage for .venv/lib/python3.13/site-packages/litellm/proxy/client/cli/commands/encryption.py: 0%

22 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 12:01 +0000

1"""CLI commands for the at-rest credential encryption migration.""" 

2 

3from typing import Final 

4 

5import click 

6import rich 

7 

8from ...http_client import HTTPClient 

9 

10 

11@click.group() 

12def encryption(): 

13 """Migrate at-rest credentials to AES-256-GCM and attest residual state.""" 

14 

15 

16@encryption.command(name="migrate") 

17@click.option( 

18 "--check", 

19 "check_only", 

20 is_flag=True, 

21 default=False, 

22 help="Read-only residual scan (no writes). Reports legacy values remaining.", 

23) 

24@click.option( 

25 "--dry-run", 

26 is_flag=True, 

27 default=False, 

28 help="Run the full migration walkers without writing any changes.", 

29) 

30@click.pass_context 

31def migrate(ctx: click.Context, check_only: bool, dry_run: bool): 

32 """Re-encrypt at-rest credentials into the AES-256-GCM (v2:gcm:) format. 

33 

34 Requires the proxy to be started with 

35 ``general_settings.encryption_algorithm: aes-256-gcm``. Idempotent and 

36 resumable; safe to re-run after an interruption. 

37 

38 Examples: 

39 lite encryption migrate --check # attestation scan, no writes 

40 lite encryption migrate # perform the migration 

41 """ 

42 client: Final = HTTPClient(ctx.obj["base_url"], ctx.obj["api_key"]) 

43 

44 if check_only: 

45 response = client.request("GET", "/credentials/migrate-encryption/check") 

46 else: 

47 response = client.request( 

48 "POST", 

49 "/credentials/migrate-encryption", 

50 json={}, 

51 params={"dry_run": "true"} if dry_run else None, 

52 ) 

53 

54 rich.print_json(data=response) 

55 

56 report: Final = response.get("report", {}) if isinstance(response, dict) else {} 

57 residual: Final = report.get("residual_legacy") 

58 if residual is not None and residual > 0: 

59 rich.print(f"[yellow]Residual legacy values remaining: {residual}[/yellow]") 

60 elif residual == 0: 

61 rich.print("[green]No legacy values remaining (residual_legacy == 0).[/green]")