Metadata-Version: 2.4
Name: flatland-client
Version: 0.1.0
Summary: Durable, dependency-free Python client for the hosted Flatland engine. Your model is a file you own; the server is a pure function that holds nothing at rest.
Project-URL: Homepage, https://flatlandfi.com
Project-URL: Repository, https://github.com/Flatlandfi/flatland
Author-email: Flatland <info@flatlandfi.com>
License-Expression: MIT
License-File: LICENSE
Keywords: decision-modeling,financial-modeling,flatland,fpa,local-first
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Office/Business :: Financial
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# flatland-client

The durable, dependency-free Python client for the hosted [Flatland](https://flatlandfi.com) engine.

**Your model is a file you own.** It lives at `~/.flatland/models/<name>.flatland.json`
on *your* machine — versioned in your own git, diffable, re-runnable anywhere.
The hosted server is a pure function (`/api/v2/*`): the client ships the model IR
in, the server computes/transforms and returns, and **retains nothing at rest**.

This package is `pip install`-light: stdlib `urllib` only, no engine, no
fastapi/uvicorn/stripe. It is the supported durable, metered hosted path for
Python users.

## Install

```bash
pip install flatland-client
```

## Quickstart

```python
from flatland_client import create, load, http_transport

t = http_transport(api_key="fl_live_...")        # HTTPS to api.flatlandfi.com

h = create("acme_plan", t)                         # writes ~/.flatland/models/acme_plan.flatland.json
h.bulk_add([
    {"name": "revenue", "type": "Currency", "value": 1000},
    {"name": "cost", "type": "Currency", "value": 400},
    {"name": "profit", "type": "Currency", "formula": "revenue - cost"},
])
print(h.compile()["values"]["profit"]["value"])    # metered compute, nothing stored server-side

# --- new session / a new machine that has the file ---
h = load("acme_plan", t)                            # durable across restart
```

## Durability + safety contract

- **Atomic writes.** Every mutation persists the new IR with a tmp + `os.replace`
  rename, so a crash never leaves a half-written model file.
- **Fail-loud (F2).** A failed mutation (a `5xx`, or a rejected `4xx`) raises and
  **leaves the local file untouched** — a failure is never silently mistaken for
  success.
- **No server fallback.** `load()` of a missing file raises — the server never
  held your model, so it cannot supply one.
- **Envelope versioning.** The file is `{ "flatland_ir_version": 1, "ir": {...} }`.
  A file written by an older client keeps loading; a too-new envelope is refused
  before it is persisted.
- **Path-traversal-safe** model names.

Get a key at https://flatlandfi.com/install.
