Metadata-Version: 2.4
Name: constellation-node-sdk
Version: 1.2.0
Summary: L9 Constellation Node SDK — TransportPacket runtime, Gate client, and node factory
License: Proprietary
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi>=0.115.0
Requires-Dist: uvicorn[standard]>=0.32.0
Requires-Dist: pydantic>=2.9.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: cryptography>=43.0.0
Requires-Dist: prometheus-client>=0.21.0
Requires-Dist: python-json-logger>=3.2.0
Requires-Dist: pyyaml>=6.0.2
Provides-Extra: dev
Requires-Dist: build>=1.2.0; extra == "dev"
Requires-Dist: mypy>=1.11.0; extra == "dev"
Requires-Dist: pytest>=8.3.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.8; extra == "dev"
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
Requires-Dist: ruff>=0.6.0; extra == "dev"
Requires-Dist: types-PyYAML>=6.0.12; extra == "dev"
Requires-Dist: types-requests; extra == "dev"
Dynamic: license-file

# Constellation Node SDK

`constellation-node-sdk` is the canonical Python SDK for Constellation worker and orchestrator nodes.

It defines the packet-native transport contract used by the Constellation system:

- `TransportPacket` is the single canonical wire format
- all node-originated follow-up work goes back through **Gate**
- hop trace is append-only and tamper-evident
- transport integrity, lineage, provenance, and delegation are first-class protocol concepts

## What this SDK provides

- canonical `TransportPacket` models and helpers
- transport hashing, signing, verification, and validation
- Gate-only outbound client
- reusable node runtime with `/v1/execute` and `/v1/health`
- orchestrator helpers for workflow composition through Gate
- tests and examples for worker and orchestrator nodes

## Architectural rules

1. Nodes **must not** know peer node URLs.
2. Nodes **must only** send follow-up work to `GATE_URL`.
3. Gate is the sole routing authority.
4. `TransportPacket` is the only supported transport format.
5. Semantic packet changes create child packets via `derive()`.
6. Observational movement appends hop trace entries without changing `transport_hash`.

## Install

```bash
pip install -e .
For development:

pip install -e ".[dev]"
Quick start
Application — calling Gate
from constellation_node_sdk import GateClient, GateClientConfig

client = GateClient(
    GateClientConfig(
        gate_url="https://gate.internal:8000",
        local_node="erp",
        timeout_seconds=30.0,
    )
)

response = await client.execute(
    action="converge",
    payload={"entity": {"id": "org-4711"}},
    tenant="tenant-a",
    idempotency_key="erp:enrichment:run-4711",
    timeout_ms=25_000,
)
That is the whole outbound integration. The application supplies business
inputs; the SDK owns packet construction, the Gate destination, the deadline,
transport idempotency, signing, HTTP, response validation, and typed failures.
An application never builds a TransportPacket. See docs/gate-client.md and
examples/application_client/.

Gate → worker (Constellation.Gate only)
After Gate resolves a worker, the transport to it is SDK-owned as well, through
constellation_node_sdk.gate_authority. That surface is deliberately absent from
the package root and from the gate package: it is not part of an application
integration, and it accepts only packets carrying Gate's own routing authority,
so it cannot be used as a node-to-peer client. See
docs/gate-authority-transport.md.

Worker node
from constellation_node_sdk import create_node_app, register_handler

@register_handler("score")
async def handle_score(_tenant: str, payload: dict) -> dict:
    return {
        "status": "completed",
        "score": 91,
        "entity_id": payload["entity_id"],
    }

app = create_node_app(
    service_name="score-node",
    version="1.0.0",
)
Orchestrator node
from constellation_node_sdk import create_node_app, register_handler
from constellation_node_sdk.gate import GateClient, get_gate_client_config_from_env
from constellation_node_sdk.orchestrator.step_executor import StepExecutor

gate_client = GateClient(get_gate_client_config_from_env())
step_executor = StepExecutor(gate_client=gate_client, source_node="orchestrator")

@register_handler("full_pipeline")
async def handle_full_pipeline(_tenant: str, payload: dict, packet):
    enrich = await step_executor.execute_step(
        parent=packet,
        action="enrich",
        payload={"entity_id": payload["entity_id"]},
    )
    score = await step_executor.execute_step(
        parent=packet,
        action="score",
        payload={**payload, **enrich.payload},
    )
    return {
        "status": "completed",
        "entity_id": payload["entity_id"],
        "enrich": enrich.payload,
        "score": score.payload,
    }

app = create_node_app(
    service_name="orchestrator",
    version="1.0.0",
)
Environment
See .env.example for runtime configuration.

Repo structure
src/constellation_node_sdk/transport/ — transport contract and hashing

src/constellation_node_sdk/security/ — signing, verification, validation

src/constellation_node_sdk/gate/ — Gate-only client and registration

src/constellation_node_sdk/runtime/ — reusable node runtime

src/constellation_node_sdk/orchestrator/ — workflow composition helpers

contracts/ — formal contract specs

examples/ — runnable worker and orchestrator examples

tests/ — unit and integration coverage

Status
This repo is intended to be:

packet-native

Gate-routed

composable

production-oriented

strict about protocol correctness


```markdown
