Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## What does this PR change?

<!-- Describe the change. -->

## Why?

<!-- Explain the motivation. -->

## Testing

<!-- Explain how this was tested. -->

## Checklist

- [ ] I have read `CONTRIBUTING.md`.
- [ ] Every commit includes a valid DCO `Signed-off-by` line.
- [ ] I have the right to submit all material in this PR.
- [ ] I have not included secrets or confidential data.
- [ ] I have updated tests where appropriate.
- [ ] I have updated documentation where appropriate.
- [ ] I have preserved required third-party licenses and attributions.
1 change: 1 addition & 0 deletions .github/dco.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
allowOverrideAction: false
77 changes: 77 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: CI

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

permissions:
contents: read

jobs:
checks:
name: Generated code, lint & types
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- uses: astral-sh/setup-uv@v6
with:
enable-cache: true

- run: uv sync --locked

# The models come from openapi/ and the sync client from the async one. A commit that
# edits either source without regenerating would ship code the repository cannot
# reproduce, so the check regenerates and compares.
- name: Generated code is up to date
run: |
uv run python scripts/generate_models.py --check
uv run python scripts/unasync.py --check

- run: uv run ruff check .
- run: uv run ruff format --check .
- run: uv run mypy

test:
name: Tests (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v5

- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
python-version: ${{ matrix.python-version }}

- run: uv sync --locked
- run: uv run pytest --cov --cov-report=term-missing

lowest-dependencies:
name: Tests on the lowest supported dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- uses: astral-sh/setup-uv@v6
with:
python-version: "3.10"

# The lower bounds in pyproject.toml are a promise to every consumer; this is what keeps
# them honest.
- run: uv sync --resolution lowest-direct
- run: uv run pytest

build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v6
- run: uv build
- run: uvx twine check --strict dist/*
32 changes: 32 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Release

on:
push:
tags: ["v*"]

jobs:
publish:
name: Publish to PyPI
runs-on: ubuntu-latest
environment: pypi
permissions:
contents: read
id-token: write # PyPI trusted publishing: no API token is stored anywhere.
steps:
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v6

- name: The tag names the package version
run: |
version=$(sed -n 's/^__version__ = "\(.*\)"$/\1/p' src/kaitencloud/_version.py)
if [ "v$version" != "$GITHUB_REF_NAME" ]; then
echo "tag $GITHUB_REF_NAME does not match the package version $version" >&2
exit 1
fi

- run: uv sync --locked
- run: uv run pytest

- run: uv build
- run: uvx twine check --strict dist/*
- run: uv publish --trusted-publishing always
21 changes: 21 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Python
__pycache__/
*.py[cod]
*.egg-info/
build/
dist/

# Tooling
.venv/
.pytest_cache/
.mypy_cache/
.ruff_cache/
.coverage
.coverage.*
coverage.xml
htmlcov/

# What a playbook run recorded about itself
.kaiten-playbook.json

.DS_Store
53 changes: 53 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Changelog

All notable changes to this project are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html): until 1.0.0, a minor version may
change the public API, and a patch version never does.

## [0.1.0] - Unreleased

The first release of the Kaiten Python SDK, generated and tested against the contract of
[`kaitencloud/kaiten@fa7f554f`](https://github.com/kaitencloud/kaiten/commit/fa7f554fb6464f52c2a3f1ed487f4c7383c8125d).

### Added

- `KaitenClient` and `AsyncKaitenClient` for the Core API: customers, instances, licenses and
license families, entitlements and entitlement groups, usage reporting, feature flag
definitions, deployment zones, releases, components, metadata fields, service accounts,
connectors and integrations -- 98 of the contract's 107 operations. Of the nine left out,
three are the console's rule-editor tooling and four a signed-in person's notification feed
and preferences, which Kaiten's own SDK coverage excludes too; the other two are OFREP flag
evaluation, which belongs to OpenFeature (see below).
- `KaitenPlatformClient` and `AsyncKaitenPlatformClient` for the Platform API: all 10 operations.
- Configuration from arguments or from `KAITEN_BASE_URL` and `KAITEN_AUTH_TOKEN`. There is no
default API address: every deployment has its own, and the SDK never guesses where to send
a token.
- License versioning: `licenses.create()` opens a license family or adds its next version
(`family_slug`, `family_id`, `lifecycle_state`), `licenses.publish()`, `archive()` and
`unarchive()` move a version through its lifecycle, and `license_families.list()` and
`get()` resolve a family to the version it currently serves, or to a pinned one.
- Models for every response and every webhook event, generated from the contract with
pydantic v2: snake_case attributes, lenient about fields and enum values added after a
release, and `to_dict()` for the exact wire shape.
- `kaitencloud.types.Scope`, every scope an organization token can carry, generated from the
contract and used to type `service_accounts.create_token()` and `platform.tokens.mint()`.
- List methods that walk every page, and fail with `PaginationError` rather than return a list
the API said was incomplete.
- Automatic retries with exponential backoff for requests that are safe to repeat -- never a
usage report -- honouring `Retry-After`.
- Typed errors for every failure, carrying the RFC 9457 problem: `code`, `detail`, `error_id`.
`ThresholdExceededError` for a usage report refused at the license's cap.
- A credential guard that refuses a platform credential on the Core client, an organization
token on the platform client, and a webhook secret on either, before anything is sent.
- `kaitencloud.webhooks`: Svix signature verification, and a typed model for each of the 55
published events.
- `kaitencloud.targeting`, builders for feature flag variants, rollouts and targeting rules.
- `kaitencloud.usage`, the enforcement arithmetic the API applies to a grant and its overage
allowance.
- `examples/playbook.py`, a resumable walk through a deployment of your own, step by step.

### Not included, on purpose

- Flag evaluation. Kaiten implements OFREP, so flags are evaluated with an OpenFeature SDK and
its generic OFREP provider (`openfeature-provider-ofrep`); the README shows how.
69 changes: 69 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Contributing to Kaiten SDK for Python

Thank you for contributing to the Kaiten Python SDK.

This project is licensed under the Apache License, Version 2.0.

## Developer Certificate of Origin

Kaiten uses the Developer Certificate of Origin 1.1 (DCO) for contributions.

Every commit must include a `Signed-off-by` trailer matching the commit author.

Use:

```bash
git commit -s -m "Describe your change"
```

This produces:

```text
Signed-off-by: Jane Doe <[email protected]>
```

The `-s` flag is a DCO sign-off. It is different from cryptographic commit
signing with `git commit -S`.

See [DCO.md](./DCO.md) for the full DCO text.

## Contribution rules

Please:

- keep pull requests focused;
- add or update tests when behavior changes;
- update documentation when relevant;
- do not include secrets, customer data, or confidential information;
- do not submit code or assets that you do not have the right to contribute;
- preserve required third-party license and attribution notices.

Accepted contributions are contributed under Apache-2.0.

## Before you open a pull request

The development workflows are [Task](https://taskfile.dev) targets over
[uv](https://docs.astral.sh/uv/); the README's
[Development](./README.md#development) section lists them all.

```bash
task setup # install the development environment
task check # everything CI runs
```

Every Python file opens with the project's license notice:

```python
# Copyright 2026 KAITEN INC
# SPDX-License-Identifier: Apache-2.0
```

The tests check it, generated files included. Never edit the generated
`src/kaitencloud/_sync/` or `src/kaitencloud/types/` modules by hand: change
the async source, the contract or a generator, then run `task generate`.

## Security

Do not report unpatched vulnerabilities in public issues.

See [SECURITY.md](./SECURITY.md).
37 changes: 37 additions & 0 deletions DCO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Developer Certificate of Origin
Version 1.1

Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.


Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or

(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or

(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.

(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
Loading
Loading