Metadata-Version: 2.4
Name: forktex-cloud
Version: 0.2.4
Summary: Typed Python SDK for the ForkTex Cloud platform — provision, deploy, and manage VPS-backed apps via a declarative manifest.
License-Expression: MIT
License-File: LICENSE
Keywords: forktex,cloud,deployment,vps,hetzner,ansible,blue-green,iac,infrastructure-as-code,sdk
Author: ForkTex
Author-email: info@forktex.com
Requires-Python: >=3.11
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Distributed Computing
Classifier: Topic :: System :: Installation/Setup
Classifier: Topic :: System :: Systems Administration
Classifier: Typing :: Typed
Requires-Dist: cryptography (>=42.0)
Requires-Dist: httpx (>=0.27.0,<1.0.0)
Requires-Dist: pydantic[email] (>=2.11.10,<3.0.0)
Requires-Dist: pyyaml (>=6.0)
Project-URL: Changelog, https://github.com/forktex/cloud/blob/master/sdk-py/CHANGELOG.md
Project-URL: Documentation, https://github.com/forktex/cloud/tree/master/docs
Project-URL: Homepage, https://forktex.com
Project-URL: Issues, https://github.com/forktex/cloud/issues
Project-URL: Repository, https://github.com/forktex/cloud
Description-Content-Type: text/markdown

# forktex-cloud

[![PyPI](https://img.shields.io/pypi/v/forktex-cloud.svg)](https://pypi.org/project/forktex-cloud/)
[![Python](https://img.shields.io/pypi/pyversions/forktex-cloud.svg)](https://pypi.org/project/forktex-cloud/)
[![License](https://img.shields.io/pypi/l/forktex-cloud.svg)](https://github.com/forktex/cloud/blob/master/sdk-py/LICENSE)

Standalone Python SDK for the [ForkTex Cloud](https://cloud.forktex.com) platform.

`forktex-cloud` is the typed `httpx` client + manifest plumbing that the `forktex` CLI uses to talk to the ForkTex Cloud control plane: project provisioning, server management, deployments, vault, manifest validation, and the docker-compose / Hetzner / Ansible bridge.

You can use it directly from any Python application — no `forktex` CLI required.

## Install

```bash
pip install forktex-cloud
```

Requires Python ≥ 3.11.

## Quick Start

### Authenticated client

```python
from forktex_cloud import ForktexCloudClient, CloudContext

ctx = CloudContext(controller="https://cloud.forktex.com", account_key="ftx-...")
with ForktexCloudClient.from_context(ctx) as client:
    projects = client.list_projects()
    servers  = client.list_servers()
    health   = client.health()
```

### Direct auth

```python
from forktex_cloud import ForktexCloudClient

# JWT bearer (user login)
with ForktexCloudClient("https://cloud.forktex.com", access_token="eyJ...") as client:
    me = client.me()

# Org-scoped API key (CI/CD)
with ForktexCloudClient(
    "https://cloud.forktex.com",
    account_key="ftx-...",
    org_id="00000000-0000-0000-0000-000000000001",
) as client:
    events = client.list_events(project_id="...")
    status = client.server_status("server-uuid")
```

### Local dev (point at your `make local` stack)

```python
client = ForktexCloudClient(
    base_url="http://localhost:8000",
    account_key="ftx-dev-key-2026",
    org_id="<your-org-uuid>",
)
```

### Trigger a deploy pipeline

```python
# Full up (provision + bootstrap + deploy + DNS + SSL)
job = client.up(project_dir=Path("./my-project"))      # reads forktex.json
print(job.job_id, job.deployment_id, job.status)

# Code push to an existing server (no re-provision)
job = client.deploy(server_id="...", service="api")    # Ansible deploy tag

# Tear down
job = client.down(keep_dns=True)                       # preserves DNS record
```

### Manifest loading + validation

```python
from forktex_cloud import Manifest, ManifestError

# Validates eagerly against the canonical Pydantic schema at construction
try:
    m = Manifest.load("forktex.json", env="production")
except ManifestError as e:
    print(f"Invalid manifest: {e}")
    raise

# Typed access to the cloud block (discriminated union over all 4 kinds)
print(m.cloud.kind)                     # "ProjectDeployment" | "StaticSite" | ...
print(m.cloud.metadata.name)
for svc in m.services_for_env(env="production"):
    print(svc.id, svc.type, svc.image)

# Wire format round-trips cleanly (model_dump → parse_cloud_block)
as_dict = m.cloud.model_dump(by_alias=True, exclude_none=True)
```

### Secrets vault (server-side, org-scoped)

```python
client.vault_set("POSTGRES_PASSWORD", "hunter2")
secret = client.vault_get("POSTGRES_PASSWORD")           # -> VaultGetResponse
keys   = client.vault_list()                             # -> list[str]
client.vault_delete("POSTGRES_PASSWORD")
```

## What's in the package

| Module | Purpose |
|---|---|
| `forktex_cloud.client` | Typed sync httpx client (`ForktexCloudClient`) + all OpenAPI-codegenned Pydantic models (`ServerRead`, `ProjectRead`, `EventRead`, `VaultGetResponse`, ...) |
| `forktex_cloud.manifest` | `Manifest` loader, discriminated-union schema (v1beta2), deep-merge for env overlays, `ManifestError` |
| `forktex_cloud.config` | `CloudContext` — controller URL, JWT / account-key, current org + project keys |
| `forktex_cloud.scaffold` | `forktex cloud init` template generator (ProjectDeployment / StaticSite / SingleContainer / NativeBuild) |
| `forktex_cloud.bridge` | docker-compose generator (local mode), Loki config, log formatters used by `forktex cloud up --env local` |
| `forktex_cloud.secrets` | Fernet vault + `${vault:KEY}` resolver for compile-time secret injection |
| `forktex_cloud.paths` | Cross-platform `.forktex/` + `~/.forktex/` filesystem spec (V1). See [docs/forktex-directory-spec.md](https://github.com/forktex/cloud/blob/master/docs/forktex-directory-spec.md) |

All response models come from the OpenAPI codegen pipeline — **one source of truth** shared between the server and every consumer. No hand-written model drift.

## Top-level re-exports

```python
from forktex_cloud import (
    # Client
    ForktexCloudClient, CloudAPIError,
    # Config
    CloudContext,
    # Manifest
    Manifest, ManifestError,
    # Response models (from OpenAPI codegen)
    ApiKeyCreated, ApiKeyRead,
    EnvironmentRead, EventRead,
    HealthRead, JobResponse, MeResponse,
    OrgRead, ProjectRead, ServerRead, UserRead,
    StatusResponse, TokenResponse, VaultGetResponse,
    WorkspaceRead,
)
```

## Versioning

The SDK follows [SemVer](https://semver.org/). The client's response models are generated from the server's OpenAPI spec at a fixed `SPEC_VERSION` + `SPEC_HASH` (inspectable at runtime via `forktex_cloud.client.generated.SPEC_VERSION`). When the server OpenAPI changes, the SDK is regenerated and released with a bumped version.

## Repository

This SDK lives inside the [`forktex/cloud`](https://github.com/forktex/cloud) monorepo alongside the API server (`api/`) and React Native client (`client/`). The SDK package is independently versioned and published to PyPI.

- Docs: [https://github.com/forktex/cloud/tree/master/docs](https://github.com/forktex/cloud/tree/master/docs)
- Production runbook: [production-runbook.md](https://github.com/forktex/cloud/blob/master/docs/production-runbook.md)
- Issues: [https://github.com/forktex/cloud/issues](https://github.com/forktex/cloud/issues)

## License

MIT — see [LICENSE](LICENSE).

