Metadata-Version: 2.4
Name: kontiki
Version: 2.0.0
Summary: Microservices framework in Python.
License: Apache-2.0
License-File: LICENSE
Author: Julien Weber
Author-email: jpkwbr@protonmail.com
Requires-Python: >=3.11,<4.0
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Dist: PyYAML (>=6.0,<7.0)
Requires-Dist: aio-pika (>=9.0.0,<10.0.0)
Requires-Dist: aiohttp (>=3.11.0,<4.0.0)
Requires-Dist: croniter (>=6.0.0,<7.0.0)
Requires-Dist: pydantic (>=2.0.0,<3.0.0)
Project-URL: Homepage, https://github.com/kontiki-org/kontiki
Project-URL: Repository, https://github.com/kontiki-org/kontiki
Description-Content-Type: text/markdown

<img src="./assets/kontiki_logo.png" width="500">

> **Part of the Kontiki suite** — Python runtime, registry, terminal UI, and
> fleet monitoring for distributed services.
>
> Full suite overview → https://kontiki-org.github.io/
>
> Ops demo → [kontiki-monitor Quickstart](https://github.com/kontiki-org/kontiki-monitor#quickstart--demo-app--telegram).

---
## Overview

**Kontiki** is a Python runtime for distributed services on RabbitMQ. You write
`@http`, `@rpc`, `@on_event`, and `@task`. The decorators are the visible API;
most of the leverage sits one layer deeper — identity, routing, delivery, fleet
health, configuration, and testing. You express **intentions in Kontiki terms**,
not broker topology by hand.

This repository is the runtime. Together with the registry,
[KontikiTUI](https://github.com/kontiki-org/kontiki-tui), and
[kontiki-monitor](https://github.com/kontiki-org/kontiki-monitor), every Python
workload — API, worker, cron job, ops service — is the same operable, testable,
and observable unit.

Most applications don't need the full depth of a specialized ops stack.
Kontiki takes the commonly useful part and aims to integrate it into the same service model,
instead of giving you another platform to assemble and tune.

### Why conventions

Services should differ primarily because of what they do, not because each one
introduces another way of being built and operated.

- An ops job fails months later: find the service in KontikiTUI instead of
  rediscovering its crontab, script, and log file.
- A request failed after crossing several services: follow its `flow_id`
  tree in KontikiTUI instead of correlating timestamps across logs by hand.
- Adding a new service — including a monitor: identity, configuration,
  lifecycle, logging, health, and failure reporting already follow the same
  conventions.

### What Kontiki is not

Kontiki does not aim for maximum infrastructure choice. It is intentionally
Python-first, RabbitMQ-based, and convention-driven. It does not abstract every
broker, become a language-neutral platform, replace orchestration, or reproduce
a tracing, long-term metrics, or IAM product. Those can coexist outside the
common model; they are not the default for everyday needs.

### Runtime

- **One model from dev to production**: merged YAML config, `cli.run`, and the
  same entrypoint decorators in tests (`kontiki.testing` mocks on the bus) and
  in production.
- **Design interactions as messages**: RPC for request/reply; events for async
  work. On `@on_event`, delivery is explicit — **default** (one instance per
  message, competing consumers), **`broadcast=True`** (every instance),
  **`in_session=True`** (one pinned instance via a session).
- **Route by `event_type`**: the event name is the routing contract; deployment
  identity (`kontiki.service_name`, `kontiki.peers`) and environment-specific
  names live in config, not in code.
- **Fleet registry**: heartbeats, degraded state (`degraded_on`), exception
  tracking, live instance ids (`list_instances` / `GET /instances/{service_name}`),
  and orchestrator live probes (`GET /live/{service_name}`). The bus
  runs without a registry; operating the fleet coherently assumes one.
- **Integrated operations**: browse the fleet in
  [**KontikiTUI**](https://github.com/kontiki-org/kontiki-tui), alert from
  registry signals with [**kontiki-monitor**](https://github.com/kontiki-org/kontiki-monitor).

For gotchas, controlled failures (`rpc_error`), and patterns beyond this
overview, see `docs/advanced-features.md`. For a feature-by-feature reference,
see `docs/features.md`.

### Mental model (short)

| Need | Reach for |
|------|-----------|
| Sync API | `@http` / `@rpc` |
| Async reaction | `@on_event` |
| Time-driven work | `@task` |
| One instance handles an event | default `@on_event` (competing consumers) |
| Every instance handles it | `@on_event(..., broadcast=True)` |
| One pinned instance | `@on_event(..., in_session=True)` + `open_session` |
| RPC to one process | `call(..., instance_id=)` / `RpcProxy(..., instance_id=)` |
| Route an event | explicit `event_type` |
| Caller target from deploy config | `RpcProxy(..., peer="…")` / `open_session(peer="…")` → `kontiki.peers` |
| Fleet health | registry + `degraded_on` |
| Cross-service debug | `flow_id` → KontikiTUI Flows (tree, exception, export) |
| Tests on the bus | `kontiki.testing` |
| Gateway into Kontiki from FastAPI, etc. | standalone `Messenger` |

---

## Kontiki suite

Kontiki is not only this runtime. The suite shares the same process model,
registry, and terminal UI:

| Component | Role |
|-----------|------|
| **Kontiki** (this repo) | Service runtime — entrypoints, messaging, registry client, config, testing |
| [**kontiki-tui**](https://github.com/kontiki-org/kontiki-tui) | Terminal UI over the registry and local logs — browse services, flows, and exceptions |
| [**kontiki-monitor**](https://github.com/kontiki-org/kontiki-monitor) | Fleet checks, registry signals, and host disk alerts |

When services register with the **Kontiki registry**, KontikiTUI gives a live
picture of the fleet from the terminal:

<img src="./assets/services.png" alt="KontikiTUI — services overview" width="900">

---

## Quickstart

Install Kontiki (via pip or Poetry):

```bash
pip install kontiki
```

Define a simple service. The **service class** wires entrypoints; a
**delegate** holds business logic (recommended pattern — see `docs/features.md`):

```python
from kontiki.delegate import ServiceDelegate
from kontiki.messaging import Messenger, on_event, rpc
from kontiki.runner import cli


class MyDelegate(ServiceDelegate):
    async def setup(self):
        pass  # optional: init from self.container.config

    def process(self, payload):
        return {"processed": payload}


class MyService:
    name = "compute-api"  # optional: overridden by kontiki.service_name in config
    delegate = MyDelegate()
    messenger = Messenger()

    @rpc
    async def compute(self, x):
        return self.delegate.process(x)

    @on_event("example.thing.happened")
    async def on_thing(self, payload):
        result = self.delegate.process(payload)
        await self.messenger.publish("example.thing.processed", result)


def run():
    cli.run(MyService, "Example Kontiki service.", version="0.1.0")
```

Expose it as a CLI command in `pyproject.toml`:

```toml
[tool.poetry.scripts]
my_service = "myapp.main:run"
```

Run your service:

```bash
my_service --config config.yaml
```

RPC plus a chained event — the shape most meshes grow from. In production,
peers resolve from config (`kontiki.peers`), delivery modes are set on handlers,
and the registry tracks the fleet. See `examples/events/broadcast/`,
`examples/events/session/`, and `examples/registry/`.

---

## Testing a service in isolation

`kontiki.testing` lets you exercise one service on the bus without running
its peers. The usual form is a Gherkin feature: an executable spec of the
service boundary.

The feature shows the timeline — config and initial state, incoming events
or RPC, calls to peers and their controlled responses, published events,
return values, state changes. That sequence stays in the `.feature`, not in
Python fixtures.

From Boomerang's subscription-service: the service starts from its config,
an RPC comes in, matching recipients come out.

```gherkin
Scenario: Return matching recipient for exact area and category
  Given the subscription service is running with the following configuration
    """
    kontiki:
      amqp:
        url: amqp://guest:guest@localhost/
      http:
        address: 127.0.0.1
        port: 8000
    logging:
      directory: logs
      handlers:
        file:
          class: logging.handlers.RotatingFileHandler
          level: INFO
          maxBytes: 10485760
          backupCount: 5
      root:
        handlers: [file]
        level: INFO
    app:
      subscriptions:
        usr_1:
          wind-fr69:
            status: active
            subscription:
              rule:
                category: weather.wind
                event_type: "*"
                criteria:
                  all_of:
                    - key: area.zone
                      operator: eq
                      value: FR-69
              endpoints:
                - email.email_primary
    """
  When I call the RPC get_recipients_for_alert on the subscription service with the following arguments
    """
    {
      "alert": {
        "schema_version": "1.0",
        "alert_id": "wx_wind_fr69",
        "source": "test",
        "category": "weather.wind",
        "event_type": "hail",
        "severity": "moderate",
        "occurred_at": "2026-01-15T12:00:00Z",
        "title": "Wind warning",
        "body": "Strong winds expected in FR-69.",
        "areas": [
          {"type": "zone", "value": "FR-69"}
        ],
        "attributes": {}
      }
    }
    """
  Then the RPC response is
    """
    [
      {
        "recipient_id": "usr_1",
        "channel": "email",
        "endpoint_key": "email_primary"
      }
    ]
    """
```

Mocks, controlled return values, and message capture live in
`kontiki.testing`; Behave steps drive them. The feature is the behavioural
source of truth. Steps execute it; they should not invent behaviour missing
from it.

See `docs/features.md` (Testing) and `tests/integration/`.

---

## Documentation

- Features: `docs/features.md`
- Advanced features (patterns & gotchas): `docs/advanced-features.md`
- Configuration reference: `docs/configuration.md`
- Example configuration: `docs/kontiki-config.example.yaml`
- Contributing guidelines: `CONTRIBUTING.md`
- License: `LICENSE`

Kontiki requires **RabbitMQ ≥ 4.3** (for V2.0+ features like quorum queues). You do not declare exchanges or queues yourself —
decorators and config declare the topology. To start a broker locally:

```bash
make run-amqp
```

---

## Examples

Examples can be run via the `Makefile` (see targets such as `run-rpc-service`,
`run-rpc-example`, `run-rpc-instance-example`, `run-simple-events-service`,
`run-task-service`, etc.). Shared logging is `examples/common.yaml`: console
`StreamHandler`, default Kontiki formatter (omit `formatters` in YAML).

| Feature                                      | Example path                                                     |
|----------------------------------------------|------------------------------------------------------------------|
| Basic RPC                                    | `examples/rpc/`                                                  |
| Targeted RPC                                 | `examples/rpc/`                                                  |
| Simple events                                | `examples/events/simple/`                                        |
| Broadcast events                             | `examples/events/broadcast/`                                     |
| Event serialization                          | `examples/events/serialization/`                                 |
| Session-based events                         | `examples/events/session/`                                       |
| Periodic tasks (interval and cron)           | `examples/task/`                                                 |
| Service registry (admin + client)            | `examples/registry/`                                             |
| Heartbeats & degraded mode                   | `examples/heartbeat/`                                            |
| HTTP entrypoints                             | `examples/http/simple/`                                          |

---

## Misc

*Kontiki did not come out of a naming workshop but from the album [*Kontiki*](https://cottonmather.bandcamp.com/album/kontiki) by the band Cotton Mather.
If you enjoy vintage 4-track indie pop as much as microservices, you should check it out.*

