blockmachine 0.8.3__tar.gz → 0.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {blockmachine-0.8.3 → blockmachine-0.9.0}/PKG-INFO +1 -1
  2. {blockmachine-0.8.3 → blockmachine-0.9.0}/blockmachine.egg-info/PKG-INFO +1 -1
  3. {blockmachine-0.8.3 → blockmachine-0.9.0}/commands/auth.py +2 -2
  4. {blockmachine-0.8.3 → blockmachine-0.9.0}/commands/miner.py +51 -4
  5. {blockmachine-0.8.3 → blockmachine-0.9.0}/commands/validator.py +1 -1
  6. {blockmachine-0.8.3 → blockmachine-0.9.0}/config.py +1 -1
  7. {blockmachine-0.8.3 → blockmachine-0.9.0}/pyproject.toml +1 -1
  8. {blockmachine-0.8.3 → blockmachine-0.9.0}/settings.py +7 -1
  9. {blockmachine-0.8.3 → blockmachine-0.9.0}/tests/test_cli_config.py +3 -2
  10. {blockmachine-0.8.3 → blockmachine-0.9.0}/__init__.py +0 -0
  11. {blockmachine-0.8.3 → blockmachine-0.9.0}/auth_config.py +0 -0
  12. {blockmachine-0.8.3 → blockmachine-0.9.0}/blockmachine.egg-info/SOURCES.txt +0 -0
  13. {blockmachine-0.8.3 → blockmachine-0.9.0}/blockmachine.egg-info/dependency_links.txt +0 -0
  14. {blockmachine-0.8.3 → blockmachine-0.9.0}/blockmachine.egg-info/entry_points.txt +0 -0
  15. {blockmachine-0.8.3 → blockmachine-0.9.0}/blockmachine.egg-info/requires.txt +0 -0
  16. {blockmachine-0.8.3 → blockmachine-0.9.0}/blockmachine.egg-info/top_level.txt +0 -0
  17. {blockmachine-0.8.3 → blockmachine-0.9.0}/client.py +0 -0
  18. {blockmachine-0.8.3 → blockmachine-0.9.0}/commands/__init__.py +0 -0
  19. {blockmachine-0.8.3 → blockmachine-0.9.0}/commands/leaderboard.py +0 -0
  20. {blockmachine-0.8.3 → blockmachine-0.9.0}/endpoints.py +0 -0
  21. {blockmachine-0.8.3 → blockmachine-0.9.0}/main.py +0 -0
  22. {blockmachine-0.8.3 → blockmachine-0.9.0}/setup.cfg +0 -0
  23. {blockmachine-0.8.3 → blockmachine-0.9.0}/testing.py +0 -0
  24. {blockmachine-0.8.3 → blockmachine-0.9.0}/tests/test_cli_endpoints_and_testing.py +0 -0
  25. {blockmachine-0.8.3 → blockmachine-0.9.0}/tests/test_leaderboard_cli.py +0 -0
  26. {blockmachine-0.8.3 → blockmachine-0.9.0}/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blockmachine
3
- Version: 0.8.3
3
+ Version: 0.9.0
4
4
  Summary: Blockmachine CLI - Miner and validator operator interface
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/taostat/blockmachine
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blockmachine
3
- Version: 0.8.3
3
+ Version: 0.9.0
4
4
  Summary: Blockmachine CLI - Miner and validator operator interface
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/taostat/blockmachine
@@ -26,7 +26,7 @@ def device_login(
26
26
 
27
27
  Raises typer.Exit(1) on failure.
28
28
  """
29
- client_id = client_id or settings.CLIENT_ID
29
+ client_id = client_id or settings.client_id()
30
30
  auth_url = auth_url or settings.auth_url()
31
31
 
32
32
  try:
@@ -157,7 +157,7 @@ def save_token(
157
157
 
158
158
  config = load_config()
159
159
  config.set_role(role)
160
- config.client_id = client_id or settings.CLIENT_ID
160
+ config.client_id = client_id or settings.client_id()
161
161
  config.auth_url = auth_url or settings.auth_url()
162
162
  config.api_url = api_url or settings.api_url()
163
163
  config.access_token = access_token
@@ -457,13 +457,17 @@ def update(
457
457
  alias: str = typer.Argument(None, help="Node alias or ID"),
458
458
  new_alias: str = typer.Option(None, "--alias", "-a", help="New alias"),
459
459
  endpoint: str = typer.Option(None, "--endpoint", "-e", help="New endpoint"),
460
+ repin: bool = typer.Option(
461
+ False, "--repin", help="Re-detect and update TLS cert fingerprint"
462
+ ),
460
463
  ) -> None:
461
- """Update a node's alias or endpoint."""
464
+ """Update a node's alias, endpoint, or TLS fingerprint."""
462
465
  alias = _get_alias(alias)
463
466
 
464
- if not new_alias and not endpoint:
467
+ if not new_alias and not endpoint and not repin:
465
468
  console.print(
466
- "[yellow]Nothing to update. Specify --alias or --endpoint[/yellow]"
469
+ "[yellow]Nothing to update."
470
+ " Specify --alias, --endpoint, or --repin[/yellow]"
467
471
  )
468
472
  return
469
473
 
@@ -476,6 +480,32 @@ def update(
476
480
 
477
481
  with RegistryClient(config) as client:
478
482
  node_id = _resolve_node(client, alias)
483
+
484
+ if repin:
485
+ node_resp = client.get(f"/nodes/{node_id}")
486
+ if node_resp.status_code != 200:
487
+ console.print(f"[red]Error fetching node:[/red] {node_resp.text}")
488
+ raise typer.Exit(1)
489
+ node_endpoint = endpoint or node_resp.json()["endpoint"]
490
+ if not is_ip_endpoint(node_endpoint):
491
+ console.print(
492
+ "[yellow]--repin ignored:"
493
+ " only applies to IP-based endpoints[/yellow]"
494
+ )
495
+ else:
496
+ fp = get_cert_fingerprint(node_endpoint)
497
+ if fp:
498
+ update_data["cert_fingerprint"] = fp
499
+ console.print(f"[dim]New TLS fingerprint: {fp}[/dim]")
500
+ else:
501
+ console.print(
502
+ "[red]Could not fetch TLS certificate from endpoint[/red]"
503
+ )
504
+ raise typer.Exit(1)
505
+
506
+ if not update_data:
507
+ return
508
+
479
509
  response = client.patch(f"/nodes/{node_id}", json=update_data)
480
510
 
481
511
  if response.status_code == 200:
@@ -522,6 +552,7 @@ def test(
522
552
  automatically. Use --endpoint and --secret for pre-registration
523
553
  testing.
524
554
  """
555
+ stored_fingerprint: Optional[str] = None
525
556
  if not endpoint:
526
557
  alias = _get_alias(alias)
527
558
  config = load_config()
@@ -533,6 +564,7 @@ def test(
533
564
  raise typer.Exit(1)
534
565
  node = response.json()
535
566
  endpoint = node["endpoint"]
567
+ stored_fingerprint = node.get("cert_fingerprint")
536
568
  if not secret:
537
569
  secret = _fetch_active_secret(client, node_id)
538
570
  console.print(f"[bold]Alias:[/bold] {alias}")
@@ -555,12 +587,27 @@ def test(
555
587
  if tls_ok:
556
588
  is_ip = is_ip_endpoint(endpoint)
557
589
  mode = "pinned" if is_ip else "CA-verified"
558
- fp_short = fingerprint[:16] + "..." if fingerprint else "none"
590
+ fp_short = _truncate(fingerprint, 16) if fingerprint else "none"
559
591
  console.print(f"[green]OK[/green] ({mode}, {fp_short})")
560
592
  else:
561
593
  console.print(f"[red]FAIL[/red] {tls_msg}")
562
594
  all_ok = False
563
595
 
596
+ if stored_fingerprint and fingerprint:
597
+ console.print("Cert pinning... ", end="")
598
+ if stored_fingerprint == fingerprint:
599
+ console.print("[green]OK[/green] (matches registry)")
600
+ else:
601
+ console.print("[red]MISMATCH[/red]")
602
+ console.print(
603
+ f"[yellow] ↳ Registry: {_truncate(stored_fingerprint, 16)}[/yellow]"
604
+ )
605
+ console.print(
606
+ f"[yellow] ↳ Live: {_truncate(fingerprint, 16)}[/yellow]"
607
+ )
608
+ console.print("[yellow] ↳ Run 'bm miner update --repin' to fix[/yellow]")
609
+ all_ok = False
610
+
564
611
  # 2. Health
565
612
  console.print("Health check... ", end="")
566
613
  health_ok, health_msg = test_health(hostname)
@@ -20,7 +20,7 @@ def login(
20
20
  ),
21
21
  ) -> None:
22
22
  """Login with validator scopes for the verification gateway."""
23
- resolved_client_id = client_id or settings.CLIENT_ID
23
+ resolved_client_id = client_id or settings.client_id()
24
24
  resolved_auth_url = auth_url or settings.auth_url()
25
25
 
26
26
  token_data = device_login(
@@ -126,7 +126,7 @@ class CLIConfig:
126
126
 
127
127
  @property
128
128
  def client_id(self) -> str:
129
- return self._net().client_id or settings.CLIENT_ID
129
+ return self._net().client_id or settings.client_id()
130
130
 
131
131
  @client_id.setter
132
132
  def client_id(self, value: Optional[str]) -> None:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "blockmachine"
7
- version = "0.8.3"
7
+ version = "0.9.0"
8
8
  description = "Blockmachine CLI - Miner and validator operator interface"
9
9
  requires-python = ">=3.10"
10
10
  license = "MIT"
@@ -15,18 +15,21 @@ class NetworkConfig:
15
15
  auth_url: str
16
16
  api_url: str
17
17
  netuid: int
18
+ client_id: str
18
19
 
19
20
 
20
21
  MAINNET = NetworkConfig(
21
22
  auth_url="https://auth.taostats.io",
22
23
  api_url="https://api.blockmachine.io",
23
24
  netuid=19,
25
+ client_id="35cc50f5-6bf5-432b-8892-84c3b0e856b2",
24
26
  )
25
27
 
26
28
  TESTNET = NetworkConfig(
27
29
  auth_url="https://test-auth.taostats.io",
28
30
  api_url="https://testnet-registry.blockmachine.io",
29
31
  netuid=417,
32
+ client_id="07f5c729-5ca7-412a-b5e7-4966e132548e",
30
33
  )
31
34
 
32
35
  # Active network — set by --testnet flag via main.py callback
@@ -57,10 +60,13 @@ def netuid() -> int:
57
60
  return _network.netuid
58
61
 
59
62
 
60
- CLIENT_ID = os.environ.get("BM_CLIENT_ID", "07f5c729-5ca7-412a-b5e7-4966e132548e")
61
63
  AUDIENCE = "bittensor-apps"
62
64
 
63
65
 
66
+ def client_id() -> str:
67
+ return os.environ.get("BM_CLIENT_ID", _network.client_id)
68
+
69
+
64
70
  def jwks_uri() -> str:
65
71
  return f"{auth_url()}/.well-known/jwks.json"
66
72
 
@@ -71,7 +71,7 @@ class TestSettings:
71
71
  s.set_network(s.MAINNET)
72
72
  assert s.auth_url() == "https://auth.taostats.io"
73
73
  assert s.api_url() == "https://api.blockmachine.io"
74
- assert s.CLIENT_ID == "07f5c729-5ca7-412a-b5e7-4966e132548e"
74
+ assert s.client_id() == "35cc50f5-6bf5-432b-8892-84c3b0e856b2"
75
75
  assert s.jwks_uri() == ("https://auth.taostats.io/.well-known/jwks.json")
76
76
  assert s.miner_scopes() == ["subnet:19:miner"]
77
77
  assert s.validator_scopes() == ["subnet:19:validator"]
@@ -101,7 +101,8 @@ class TestSettings:
101
101
 
102
102
  assert s.auth_url() == "https://custom-auth.example.com"
103
103
  assert s.api_url() == "https://custom-api.example.com"
104
- assert s.CLIENT_ID == "custom-client-id"
104
+ s.set_network(s.MAINNET)
105
+ assert s.client_id() == "custom-client-id"
105
106
  assert s.jwks_uri() == (
106
107
  "https://custom-auth.example.com/.well-known/jwks.json"
107
108
  )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes