Metadata-Version: 2.4
Name: odysseys-py
Version: 0.0.3
Summary: A lightweight durable execution and orchestration runtime for distributed services.
Author: Sreejay Reddy
License: MIT License
        
        Copyright (c) 2026 Sreejay Reddy
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: psycopg[binary]>=3.2
Requires-Dist: PyYAML>=6.0
Requires-Dist: python-dotenv>=1.0
Requires-Dist: starlette>=0.40
Requires-Dist: uvicorn>=0.30
Provides-Extra: test
Requires-Dist: pytest>=8.0; extra == "test"
Requires-Dist: pytest-asyncio>=1.0; extra == "test"
Requires-Dist: httpx2; extra == "test"
Dynamic: license-file

<p align="center">
  <img src="assets/dotted_logo_lettering.png" alt="Odyssey" width="800">
</p>

<p align="center">
  <a href="https://github.com/sreejay-reddy/odyssey">
    <img src="https://img.shields.io/github/stars/sreejay-reddy/odyssey?style=flat-square" alt="GitHub Stars">
  </a>
  <a href="https://github.com/sreejay-reddy/odyssey">
    <img src="https://img.shields.io/github/license/sreejay-reddy/odyssey?style=flat-square" alt="License">
  </a>
  <img src="https://img.shields.io/badge/Go-1.24+-00ADD8?style=flat-square&logo=go" alt="Go">
  <img src="https://img.shields.io/badge/Python-3.10+-3776AB?style=flat-square&logo=python" alt="Python">
</p>

<h2 align="center">
    Distributed durable execution engine and orchestrator <br>
    built on PostgreSQL.
</h2>

## What is Odyssey?

Odyssey is a PostgreSQL-native distributed durable execution engine and
orchestrator.

It turns PostgreSQL into the coordination layer for distributed work,
providing persistent execution state, ownership, fencing, and recovery
without requiring a separate workflow infrastructure stack.

Applications define what should execute. Odyssey manages the durable
execution of that work across services and failures.

## Why Odyssey?

Distributed applications are built from processes and services that can
fail independently. A function can finish after its caller disappears,
a worker can crash halfway through an operation, or two workers can
attempt to execute the same work.

Making this work reliable typically requires a dedicated orchestration
system, durable state store, and coordination layer.

Odyssey takes a different approach:

> **Use PostgreSQL as the durable coordination layer.**

If your application already runs on PostgreSQL, Odyssey lets you build
durable workflows around the database you already operate.

## How It Works

Odyssey turns application functions into durable executions.

### 1. Register your functions

Register the functions Odyssey is allowed to execute.

```python
from odyssey import Odyssey

client = Odyssey()

client.register(target="payments",fn=charge_card, ttl_ms=10000)
client.register(target="notifications",fn=send_confirmation, ttl_ms=9000)
```

Each registered function represents an executable step in a workflow.
the target is the stable name used to identify that function during
execution. Together, the registered targets form Odyssey's function
registry.

### 2. Define your workflow

```python
from odyssey import Odyssey, Step

client = Odyssey()

steps = [
    Step(
        target="payments",
        amount=100,
        currency="USD",
    ),
    Step(
        target="reserve_items",
        delegate="inventory",
        product_id="prod_123",
        quantity=2,
    ),
    Step(
        target="notifications",
        user = "USR_U7rq6",
    ),
]

ledger = client.build_ledger(
    key="order_123",
    steps=steps,
)
```

Each `Step` represents an executable unit of the workflow and is executed
in order.

- `target` identifies the function the step executes.
- `delegate` identifies a service configured in `odyssey.yaml` where the
  step should execute.
- Without a `delegate`, the step is treated as local and the target must
  exist in the local function registry.
- Additional keyword arguments become the step's execution input.

Every execution is uniquely identified by the (key, target) pair.
A workflow can contain many different targets under the same key, and the
same target can be used under different keys, but the same (key, target)
pair cannot be defined more than once.

### 3. Configure your services

Odyssey uses `odyssey.yaml` to define services that can receive delegated
execution.

```yaml
services:
  payments: http://localhost:9001
  inventory: http://localhost:9002
  notifications: http://localhost:9003

registry:
  default:
    retry:
      policy: forever
      delay: 2s

  inventory:
    retry:
      policy: forever
      delay: 2s
    
  payments:
    retry:
      policy: fixed
      attempts: 5
      delay: 4s
```

The `registry` section defines execution policies for registered targets,
including retry behavior.

```python
Step(
    target="reserve_items",
    delegate="inventory",
    product_id="prod_123",
    quantity=2,
)
```
When no delegate is provided, Odyssey treats the step as a local
execution and resolves the target against the local function registry.
This allows the same workflow definition to contain both local and
delegated execution.

***A workflow can therefore begin entirely locally and introduce delegated steps as functionality is extracted into independent services.***

...

### What Odyssey manages

Once a workflow is defined, Odyssey manages the execution lifecycle and
durability of each step.

- **Durable execution state** — execution state is persisted in PostgreSQL.
- **Execution ownership** — workers acquire ownership before executing a step.
- **Fencing** — stale workers are prevented from completing executions they
  no longer own.
- **Recovery** — unfinished executions can be picked up after a worker or
  process failure.
- **Inputs and results** — execution inputs and results are persisted with
  the execution state.
- **Execution timeouts** — each registered target can define a TTL for its
  execution ownership.
- **Local execution** — steps can execute directly against the local
  function registry.
- **Delegated execution** — steps can be sent to another configured service.
- **Execution identity** — `(key, target)` uniquely identifies
  an execution within a workflow.
...

## SDKs

Odyssey currently provides SDKs for:

- Python
- Go
- TypeScript [planned]

## Project Status

Odyssey is currently in early development.

The core execution engine, PostgreSQL coordination layer, and Python and Go
SDKs are available. APIs and execution semantics may continue to evolve.

## License

Odyssey is licensed under the [MIT License](LICENSE).
