Skip to content
Merged
15 changes: 4 additions & 11 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,12 @@ All notable changes to the AxonFlow Python SDK will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [6.3.0] - Release Pending (2026-04-09)
## [6.3.0] - 2026-04-09

### Added

- **Explicit IPv6 + RFC1918 boundary test coverage.** The v6.2.0 test suite relied on Python stdlib `ipaddress.is_private` for correctness and didn't assert the behavior explicitly. New cases cover:
- `172.15.0.1` and `172.32.0.1` must be `remote` (RFC1918 boundary)
- `172.16.0.0` and `172.31.255.255` must be `private_network`
- IPv6 ULA (`fd00::1`, `fd12:3456:789a::1`, `fc00::1`) → `private_network`
- IPv6 link-local (`fe80::1`) → `private_network`
- Public IPv6 (`2001:4860:4860::8888`, `2606:4700:4700::1111`) → `remote`
- IPv6 loopback `::1` → `localhost`

No runtime behavior change — Python's stdlib classifier was already correct for all these cases. The added tests are cross-SDK parity with TS/Java/Go.
- `AXONFLOW_TRY=1` environment variable to connect to `try.getaxonflow.com` shared evaluation server
- `register_try()` helper in `axonflow.community` for self-registering a tenant
- Checkpoint telemetry reports `endpoint_type: "community-saas"` when try mode is active

---

Expand Down
17 changes: 12 additions & 5 deletions axonflow/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,11 +358,18 @@ def __init__(

As of v1.0.0, all routes go through a single endpoint (Single Entry Point Architecture).
"""
# Support AXONFLOW_AGENT_URL env var for backwards compatibility
resolved_endpoint = endpoint or os.environ.get("AXONFLOW_AGENT_URL")
if not resolved_endpoint:
msg = "endpoint is required (or set AXONFLOW_AGENT_URL environment variable)"
raise TypeError(msg)
# Try mode: auto-connect to try.getaxonflow.com (must be checked before endpoint validation)
if os.environ.get("AXONFLOW_TRY") == "1":
resolved_endpoint = "https://try.getaxonflow.com"
if not client_id:
msg = "client_id is required in try mode (AXONFLOW_TRY=1)"
raise TypeError(msg)
else:
# Support AXONFLOW_AGENT_URL env var for backwards compatibility
resolved_endpoint = endpoint or os.environ.get("AXONFLOW_AGENT_URL") or ""
if not resolved_endpoint:
msg = "endpoint is required (or set AXONFLOW_AGENT_URL environment variable)"
raise TypeError(msg)

if isinstance(mode, str):
mode = Mode(mode)
Expand Down
29 changes: 29 additions & 0 deletions axonflow/community.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Community SaaS registration helper for try.getaxonflow.com."""

from __future__ import annotations

from typing import Any

import httpx

TRY_ENDPOINT = "https://try.getaxonflow.com"


def register_try(label: str = "", endpoint: str = TRY_ENDPOINT) -> dict[str, Any]:
"""Register for a free evaluation tenant on try.getaxonflow.com.

Returns dict with keys: tenant_id, secret, secret_prefix, expires_at, endpoint, note.
Store the secret securely — it is shown only once.

Args:
label: Optional human-readable name for the registration.
endpoint: Override the default endpoint (for local testing).
"""
response = httpx.post(
f"{endpoint}/api/v1/register",
json={"label": label} if label else {},
timeout=10.0,
)
response.raise_for_status()
data: dict[str, Any] = response.json()
return data
3 changes: 3 additions & 0 deletions axonflow/telemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def _classify_endpoint(url: str | None) -> str: # noqa: PLR0911
"""Classify the configured AxonFlow endpoint for analytics (#1525).

Returns one of:
``"community-saas"`` — try.getaxonflow.com shared evaluation server
``"localhost"`` — localhost, 127.0.0.1, ::1, 0.0.0.0, ``*.localhost``
``"private_network"`` — RFC1918 ranges, link-local, ``*.local``,
``*.internal``, ``*.lan``, ``*.intranet``
Expand All @@ -95,6 +96,8 @@ def _classify_endpoint(url: str | None) -> str: # noqa: PLR0911

The raw URL is never sent — only the classification. See issue #1525.
"""
if os.environ.get("AXONFLOW_TRY") == "1":
return "community-saas"
if not url:
return "unknown"
try:
Expand Down
Loading