firetail-finchly 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,27 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main, dev]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ matrix:
16
+ python: ["3.10", "3.11", "3.12", "3.13"]
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: ${{ matrix.python }}
22
+ cache: pip
23
+ cache-dependency-path: pyproject.toml
24
+ - run: pip install -e ".[dev]"
25
+ - run: python -m pytest
26
+ - name: Check the package builds
27
+ run: python -m build
@@ -0,0 +1,53 @@
1
+ name: release
2
+
3
+ # Publishes firetail_finchly when a version tag is pushed.
4
+ # Uses PyPI trusted publishing: no PyPI token is stored. See README, "Release".
5
+
6
+ on:
7
+ push:
8
+ tags: ["v*"]
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ build:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.12"
21
+
22
+ - name: Check the tag matches the package version
23
+ env:
24
+ TAG: ${{ github.ref_name }}
25
+ run: |
26
+ version="$(python -c "exec(open('src/firetail_finchly/_version.py').read()); print(__version__)")"
27
+ if [ "v${version}" != "${TAG}" ]; then
28
+ echo "Tag ${TAG} does not match package version ${version}" >&2
29
+ exit 1
30
+ fi
31
+
32
+ - run: pip install -e ".[dev]"
33
+ - run: python -m pytest
34
+ - run: python -m build
35
+
36
+ - uses: actions/upload-artifact@v4
37
+ with:
38
+ name: dist
39
+ path: dist/
40
+ if-no-files-found: error
41
+
42
+ publish:
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ environment: pypi
46
+ permissions:
47
+ id-token: write
48
+ steps:
49
+ - uses: actions/download-artifact@v4
50
+ with:
51
+ name: dist
52
+ path: dist/
53
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ venv/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ .coverage
10
+ .env
11
+ .DS_Store
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ All notable changes to `firetail_finchly`. Versions follow semver.
4
+
5
+ ## Unreleased
6
+
7
+ ## 0.0.1 - 2026-11-11
8
+
9
+ The first release.
10
+
11
+ - Package skeleton under its final name.
12
+ - `load_config`: read and validate the `PLATFORM_*` settings.
13
+ - `load_manifest`, `check_manifest`: load a manifest and check the fields
14
+ the SDK depends on.
15
+ - `PLATFORM_API_VERSION`, `SUPPORTED_PLATFORM_API_VERSIONS`, `SDK_NAME`,
16
+ `SDK_VERSION`.
17
+ - `IdentityVerifier`: verify the `x-platform-identity` token against the
18
+ gateway's published keys. Rejections are an `IdentityError` with a
19
+ `reason` from the specification. Tested against the identity token
20
+ conformance vectors.
21
+ - New dependency: `PyJWT[crypto]`.
22
+ - `ServiceClient`: call another service through the gateway, as the
23
+ service itself or for a user. Makes the instance key, registers it with
24
+ the service credential, and extends it before it expires. `url` and
25
+ `headers` let another HTTP client make the call.
26
+ - `load_config` reads `PLATFORM_SERVICE_CREDENTIAL`.
27
+ - New error codes: `service_call_invalid`, `service_key_rejected`.
@@ -0,0 +1,242 @@
1
+ Metadata-Version: 2.5
2
+ Name: firetail_finchly
3
+ Version: 0.0.1
4
+ Summary: Python SDK for the Finchly platform
5
+ Project-URL: Repository, https://github.com/firetail-io/finchly-sdk-py
6
+ Project-URL: Changelog, https://github.com/firetail-io/finchly-sdk-py/blob/main/CHANGELOG.md
7
+ Classifier: Development Status :: 2 - Pre-Alpha
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Typing :: Typed
14
+ Requires-Python: >=3.10
15
+ Requires-Dist: pyjwt[crypto]<3,>=2.8
16
+ Provides-Extra: dev
17
+ Requires-Dist: build>=1.2; extra == 'dev'
18
+ Requires-Dist: pytest>=8; extra == 'dev'
19
+ Description-Content-Type: text/markdown
20
+
21
+ # firetail_finchly
22
+
23
+ Python SDK for the [Finchly](https://github.com/firetail-io/finchly)
24
+ platform. A service or app uses it to register with a Finchly deployment,
25
+ verify who is calling it, and reach the platform's database, object store,
26
+ queue and other services.
27
+
28
+ Status: early. The package is tested, but only the first four modules
29
+ exist.
30
+
31
+ The SDK implements the platform API, which is specified in
32
+ `firetail-io/finchly` under `spec/`. This release speaks platform API `v1`
33
+ (draft).
34
+
35
+ ## Install
36
+
37
+ ```
38
+ pip install firetail_finchly
39
+ ```
40
+
41
+ Requires Python 3.10 or later. Depends on PyJWT with its `crypto` extra.
42
+
43
+ ## Use
44
+
45
+ ```python
46
+ from firetail_finchly import load_config, load_manifest
47
+
48
+ config = load_config() # reads PLATFORM_* settings
49
+ manifest = load_manifest(config.manifest_path)
50
+ ```
51
+
52
+ Both raise a `FinchlyError` with a stable `code` and a `problems` tuple that
53
+ names everything wrong, so a misconfigured container fails once with the
54
+ full picture.
55
+
56
+ ### Who is calling
57
+
58
+ The gateway puts a short-lived signed token in the `x-platform-identity`
59
+ header of every request it proxies. It is the only identity a service
60
+ should accept.
61
+
62
+ ```python
63
+ from firetail_finchly import IdentityError, IdentityVerifier
64
+
65
+ # Once, at startup. It caches the gateway's issuer and signing keys, and
66
+ # is safe to share between threads.
67
+ identity = IdentityVerifier(slug=manifest["slug"], gateway_url=config.gateway_url)
68
+
69
+ # Per request.
70
+ try:
71
+ caller = identity.from_headers(request.headers)
72
+ caller.subject # user id, or "service:<slug>"
73
+ caller.email # None for a service calling as itself
74
+ caller.groups # platform groups
75
+ caller.roles # roles for this service: "user", "operator"
76
+ caller.actor # "service:<slug>" when a service acts for the user
77
+ except IdentityError as err:
78
+ ... # Unauthenticated: respond 401. err.reason is for logs only.
79
+ ```
80
+
81
+ Any other `FinchlyError` from the verifier means the gateway could not be
82
+ reached; respond 503.
83
+
84
+ A token is rejected unless it is signed with ES256 by a key the gateway
85
+ publishes, names that gateway as issuer and this service as its only
86
+ audience, and has not expired. The rules are in `spec/identity-token.md` in
87
+ `firetail-io/finchly`.
88
+
89
+ No other header is ever consulted. On a path the manifest declares in
90
+ `publicPaths` there is no token, and the service authenticates the request
91
+ itself.
92
+
93
+ ### Calling another service
94
+
95
+ Services never address each other directly. A call goes through the
96
+ gateway, signed by this instance's own key.
97
+
98
+ ```python
99
+ from firetail_finchly import ServiceClient
100
+
101
+ # Once, at startup. Safe to share between threads.
102
+ service = ServiceClient(
103
+ slug=manifest["slug"],
104
+ gateway_url=config.gateway_url,
105
+ credential=config.service_credential,
106
+ )
107
+ service.start() # optional: fail now, not on the first call
108
+
109
+ # As the service itself.
110
+ response = service.call(
111
+ "notification-service",
112
+ "/api/send",
113
+ method="POST",
114
+ headers={"Content-Type": "application/json"},
115
+ body=b'{"text": "hello"}',
116
+ )
117
+ response.status, response.headers, response.body, response.json()
118
+
119
+ # For a user: the target sees that user, and this service acting for them.
120
+ service.call("notification-service", "/api/inbox", as_user=caller.subject)
121
+ ```
122
+
123
+ `call` returns the response, whatever its status.
124
+
125
+ - **403** means this service's manifest does not list the target in
126
+ `calls`, or the user has no access to the target.
127
+ - **A redirect is handed back, not followed.**
128
+ - **Nothing is retried.** After a 401 the next call registers the key
129
+ again, in case the gateway no longer knows it.
130
+
131
+ To use another HTTP client, such as `requests` or `httpx`, take the address
132
+ and the header from the client and send the request yourself. A token is
133
+ for one target and is valid for a minute, so get fresh headers each time:
134
+
135
+ ```python
136
+ requests.post(
137
+ service.url("notification-service", "/api/send"),
138
+ headers=service.headers("notification-service"),
139
+ json={"text": "hello"},
140
+ allow_redirects=False,
141
+ )
142
+ ```
143
+
144
+ The instance makes an ES256 key pair in memory the first time it is needed.
145
+ The private half never leaves the process. The public half is registered
146
+ with the gateway using the credential the service was deployed with, and
147
+ extended by itself for as long as the instance runs. The rules are in
148
+ `spec/service-token.md` and `spec/registration.md` in `firetail-io/finchly`.
149
+
150
+ Only a built-in service has a credential. An app's key is accepted when it
151
+ registers, which the `app` module will do; until then an app cannot use
152
+ `service`.
153
+
154
+ ### Settings
155
+
156
+ | Variable | Required | Meaning |
157
+ |---|---|---|
158
+ | `PLATFORM_GATEWAY_URL` | yes | The gateway's internal address |
159
+ | `PLATFORM_MANIFEST_PATH` | no | Manifest path in the container. Default `/finchly/manifest.json` |
160
+ | `PLATFORM_APP_ADDRESS` | no | Address the gateway should use to reach this service |
161
+ | `PLATFORM_ENROLMENT_TOKEN` | no | Token for self-registration |
162
+ | `PLATFORM_SERVICE_CREDENTIAL` | no | What a built-in service registers its instance key with |
163
+
164
+ The enrolment token and the service credential are left out of the
165
+ config's `repr`, so logging the config does not leak them.
166
+
167
+ ## Modules
168
+
169
+ The Python SDK may trail the Node SDK for modules that only Node services
170
+ use.
171
+
172
+ | Module | What it does | State |
173
+ |---|---|---|
174
+ | `config` | Read and validate the platform settings | Done |
175
+ | `manifest` | Load the manifest and check what the SDK depends on | Done |
176
+ | `identity` | Verify `x-platform-identity` | Done. Directory lookups (`list_users`, `members_of`) and `require_*` helpers are planned |
177
+ | `app` | Register with the platform, serve `/_platform/*`, renew keys, fetch settings | Next |
178
+ | `service` | Call other services through the gateway, as the service or for a user | Done for built-in services. An app's first key waits on `app` |
179
+ | `db` | PostgreSQL: queries, transactions, advisory locks, migrations | Planned |
180
+ | `blobs` | Object storage with conditional writes | Planned |
181
+ | `queue` | Publish, lease, ack, nack | Planned |
182
+ | `secrets` | Secrets and envelope encryption | Planned |
183
+ | `llm` | Model calls through the platform | Planned |
184
+ | `audit`, `usage`, `notify`, `schedule`, `connectors` | Clients for platform services | Planned |
185
+ | `ui` | Shared header, footer and theme | Not planned; Node only |
186
+
187
+ The manifest check here is not the full schema. The platform runs the full
188
+ check at registration and is the authority.
189
+
190
+ ## Develop
191
+
192
+ ```
193
+ python -m venv .venv && . .venv/bin/activate
194
+ pip install -e ".[dev]"
195
+ python -m pytest
196
+ ```
197
+
198
+ Without a local Python environment, run them in a container:
199
+
200
+ ```
201
+ docker run --rm -v "$PWD":/src:ro python:3.12-slim sh -c \
202
+ "cp -r /src /w && cd /w && pip install -q -e '.[dev]' && python -m pytest -q"
203
+ ```
204
+
205
+ ### Conformance vectors
206
+
207
+ `tests/conformance/` holds test vectors copied from `firetail-io/finchly`
208
+ (`conformance/vectors/`). Both SDKs must reach the verdict each vector
209
+ states. Do not edit them here: change them in `finchly`, regenerate, and
210
+ copy the file to both SDK repositories. Until `finchly` publishes its
211
+ conformance suite as an image, the copy is made by hand.
212
+
213
+ ## Release
214
+
215
+ Each package version follows semver, independent of the Node SDK and of the
216
+ platform.
217
+
218
+ 1. Changes land on `main` by pull request. Each one adds a line to
219
+ `CHANGELOG.md` under "Unreleased".
220
+ 2. To release, set the version in `src/firetail_finchly/_version.py`, move
221
+ the changelog entries under that version, merge, and tag the merge
222
+ commit `v` plus the version, for example `v1.2.0`.
223
+ 3. The `release` workflow checks the tag matches the version, runs the
224
+ tests, builds the sdist and wheel, and publishes.
225
+ 4. A release candidate uses the PEP 440 form, `1.2.0rc1`, tagged
226
+ `v1.2.0rc1`. pip does not install pre-releases unless asked.
227
+ 5. A bad release is yanked on PyPI and followed by a patch release.
228
+
229
+ Publishing uses PyPI trusted publishing: the workflow proves its identity to
230
+ PyPI with its GitHub Actions token, so no PyPI token is stored in this
231
+ repository.
232
+
233
+ One-time setup, not yet done:
234
+
235
+ - On PyPI, add a pending trusted publisher for the project
236
+ `firetail_finchly`: owner `firetail-io`, repository `finchly-sdk-py`,
237
+ workflow `release.yml`, environment `pypi`.
238
+ - Create a GitHub environment called `pypi` in this repository, restricted
239
+ to tags.
240
+
241
+ Still to add: the conformance suite from `firetail-io/finchly`, run in CI on
242
+ every change, once that suite exists.
@@ -0,0 +1,222 @@
1
+ # firetail_finchly
2
+
3
+ Python SDK for the [Finchly](https://github.com/firetail-io/finchly)
4
+ platform. A service or app uses it to register with a Finchly deployment,
5
+ verify who is calling it, and reach the platform's database, object store,
6
+ queue and other services.
7
+
8
+ Status: early. The package is tested, but only the first four modules
9
+ exist.
10
+
11
+ The SDK implements the platform API, which is specified in
12
+ `firetail-io/finchly` under `spec/`. This release speaks platform API `v1`
13
+ (draft).
14
+
15
+ ## Install
16
+
17
+ ```
18
+ pip install firetail_finchly
19
+ ```
20
+
21
+ Requires Python 3.10 or later. Depends on PyJWT with its `crypto` extra.
22
+
23
+ ## Use
24
+
25
+ ```python
26
+ from firetail_finchly import load_config, load_manifest
27
+
28
+ config = load_config() # reads PLATFORM_* settings
29
+ manifest = load_manifest(config.manifest_path)
30
+ ```
31
+
32
+ Both raise a `FinchlyError` with a stable `code` and a `problems` tuple that
33
+ names everything wrong, so a misconfigured container fails once with the
34
+ full picture.
35
+
36
+ ### Who is calling
37
+
38
+ The gateway puts a short-lived signed token in the `x-platform-identity`
39
+ header of every request it proxies. It is the only identity a service
40
+ should accept.
41
+
42
+ ```python
43
+ from firetail_finchly import IdentityError, IdentityVerifier
44
+
45
+ # Once, at startup. It caches the gateway's issuer and signing keys, and
46
+ # is safe to share between threads.
47
+ identity = IdentityVerifier(slug=manifest["slug"], gateway_url=config.gateway_url)
48
+
49
+ # Per request.
50
+ try:
51
+ caller = identity.from_headers(request.headers)
52
+ caller.subject # user id, or "service:<slug>"
53
+ caller.email # None for a service calling as itself
54
+ caller.groups # platform groups
55
+ caller.roles # roles for this service: "user", "operator"
56
+ caller.actor # "service:<slug>" when a service acts for the user
57
+ except IdentityError as err:
58
+ ... # Unauthenticated: respond 401. err.reason is for logs only.
59
+ ```
60
+
61
+ Any other `FinchlyError` from the verifier means the gateway could not be
62
+ reached; respond 503.
63
+
64
+ A token is rejected unless it is signed with ES256 by a key the gateway
65
+ publishes, names that gateway as issuer and this service as its only
66
+ audience, and has not expired. The rules are in `spec/identity-token.md` in
67
+ `firetail-io/finchly`.
68
+
69
+ No other header is ever consulted. On a path the manifest declares in
70
+ `publicPaths` there is no token, and the service authenticates the request
71
+ itself.
72
+
73
+ ### Calling another service
74
+
75
+ Services never address each other directly. A call goes through the
76
+ gateway, signed by this instance's own key.
77
+
78
+ ```python
79
+ from firetail_finchly import ServiceClient
80
+
81
+ # Once, at startup. Safe to share between threads.
82
+ service = ServiceClient(
83
+ slug=manifest["slug"],
84
+ gateway_url=config.gateway_url,
85
+ credential=config.service_credential,
86
+ )
87
+ service.start() # optional: fail now, not on the first call
88
+
89
+ # As the service itself.
90
+ response = service.call(
91
+ "notification-service",
92
+ "/api/send",
93
+ method="POST",
94
+ headers={"Content-Type": "application/json"},
95
+ body=b'{"text": "hello"}',
96
+ )
97
+ response.status, response.headers, response.body, response.json()
98
+
99
+ # For a user: the target sees that user, and this service acting for them.
100
+ service.call("notification-service", "/api/inbox", as_user=caller.subject)
101
+ ```
102
+
103
+ `call` returns the response, whatever its status.
104
+
105
+ - **403** means this service's manifest does not list the target in
106
+ `calls`, or the user has no access to the target.
107
+ - **A redirect is handed back, not followed.**
108
+ - **Nothing is retried.** After a 401 the next call registers the key
109
+ again, in case the gateway no longer knows it.
110
+
111
+ To use another HTTP client, such as `requests` or `httpx`, take the address
112
+ and the header from the client and send the request yourself. A token is
113
+ for one target and is valid for a minute, so get fresh headers each time:
114
+
115
+ ```python
116
+ requests.post(
117
+ service.url("notification-service", "/api/send"),
118
+ headers=service.headers("notification-service"),
119
+ json={"text": "hello"},
120
+ allow_redirects=False,
121
+ )
122
+ ```
123
+
124
+ The instance makes an ES256 key pair in memory the first time it is needed.
125
+ The private half never leaves the process. The public half is registered
126
+ with the gateway using the credential the service was deployed with, and
127
+ extended by itself for as long as the instance runs. The rules are in
128
+ `spec/service-token.md` and `spec/registration.md` in `firetail-io/finchly`.
129
+
130
+ Only a built-in service has a credential. An app's key is accepted when it
131
+ registers, which the `app` module will do; until then an app cannot use
132
+ `service`.
133
+
134
+ ### Settings
135
+
136
+ | Variable | Required | Meaning |
137
+ |---|---|---|
138
+ | `PLATFORM_GATEWAY_URL` | yes | The gateway's internal address |
139
+ | `PLATFORM_MANIFEST_PATH` | no | Manifest path in the container. Default `/finchly/manifest.json` |
140
+ | `PLATFORM_APP_ADDRESS` | no | Address the gateway should use to reach this service |
141
+ | `PLATFORM_ENROLMENT_TOKEN` | no | Token for self-registration |
142
+ | `PLATFORM_SERVICE_CREDENTIAL` | no | What a built-in service registers its instance key with |
143
+
144
+ The enrolment token and the service credential are left out of the
145
+ config's `repr`, so logging the config does not leak them.
146
+
147
+ ## Modules
148
+
149
+ The Python SDK may trail the Node SDK for modules that only Node services
150
+ use.
151
+
152
+ | Module | What it does | State |
153
+ |---|---|---|
154
+ | `config` | Read and validate the platform settings | Done |
155
+ | `manifest` | Load the manifest and check what the SDK depends on | Done |
156
+ | `identity` | Verify `x-platform-identity` | Done. Directory lookups (`list_users`, `members_of`) and `require_*` helpers are planned |
157
+ | `app` | Register with the platform, serve `/_platform/*`, renew keys, fetch settings | Next |
158
+ | `service` | Call other services through the gateway, as the service or for a user | Done for built-in services. An app's first key waits on `app` |
159
+ | `db` | PostgreSQL: queries, transactions, advisory locks, migrations | Planned |
160
+ | `blobs` | Object storage with conditional writes | Planned |
161
+ | `queue` | Publish, lease, ack, nack | Planned |
162
+ | `secrets` | Secrets and envelope encryption | Planned |
163
+ | `llm` | Model calls through the platform | Planned |
164
+ | `audit`, `usage`, `notify`, `schedule`, `connectors` | Clients for platform services | Planned |
165
+ | `ui` | Shared header, footer and theme | Not planned; Node only |
166
+
167
+ The manifest check here is not the full schema. The platform runs the full
168
+ check at registration and is the authority.
169
+
170
+ ## Develop
171
+
172
+ ```
173
+ python -m venv .venv && . .venv/bin/activate
174
+ pip install -e ".[dev]"
175
+ python -m pytest
176
+ ```
177
+
178
+ Without a local Python environment, run them in a container:
179
+
180
+ ```
181
+ docker run --rm -v "$PWD":/src:ro python:3.12-slim sh -c \
182
+ "cp -r /src /w && cd /w && pip install -q -e '.[dev]' && python -m pytest -q"
183
+ ```
184
+
185
+ ### Conformance vectors
186
+
187
+ `tests/conformance/` holds test vectors copied from `firetail-io/finchly`
188
+ (`conformance/vectors/`). Both SDKs must reach the verdict each vector
189
+ states. Do not edit them here: change them in `finchly`, regenerate, and
190
+ copy the file to both SDK repositories. Until `finchly` publishes its
191
+ conformance suite as an image, the copy is made by hand.
192
+
193
+ ## Release
194
+
195
+ Each package version follows semver, independent of the Node SDK and of the
196
+ platform.
197
+
198
+ 1. Changes land on `main` by pull request. Each one adds a line to
199
+ `CHANGELOG.md` under "Unreleased".
200
+ 2. To release, set the version in `src/firetail_finchly/_version.py`, move
201
+ the changelog entries under that version, merge, and tag the merge
202
+ commit `v` plus the version, for example `v1.2.0`.
203
+ 3. The `release` workflow checks the tag matches the version, runs the
204
+ tests, builds the sdist and wheel, and publishes.
205
+ 4. A release candidate uses the PEP 440 form, `1.2.0rc1`, tagged
206
+ `v1.2.0rc1`. pip does not install pre-releases unless asked.
207
+ 5. A bad release is yanked on PyPI and followed by a patch release.
208
+
209
+ Publishing uses PyPI trusted publishing: the workflow proves its identity to
210
+ PyPI with its GitHub Actions token, so no PyPI token is stored in this
211
+ repository.
212
+
213
+ One-time setup, not yet done:
214
+
215
+ - On PyPI, add a pending trusted publisher for the project
216
+ `firetail_finchly`: owner `firetail-io`, repository `finchly-sdk-py`,
217
+ workflow `release.yml`, environment `pypi`.
218
+ - Create a GitHub environment called `pypi` in this repository, restricted
219
+ to tags.
220
+
221
+ Still to add: the conformance suite from `firetail-io/finchly`, run in CI on
222
+ every change, once that suite exists.
@@ -0,0 +1,38 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.25"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "firetail_finchly"
7
+ dynamic = ["version"]
8
+ description = "Python SDK for the Finchly platform"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ dependencies = [
12
+ "PyJWT[crypto]>=2.8,<3",
13
+ ]
14
+ classifiers = [
15
+ "Development Status :: 2 - Pre-Alpha",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Typing :: Typed",
22
+ ]
23
+
24
+ [project.urls]
25
+ Repository = "https://github.com/firetail-io/finchly-sdk-py"
26
+ Changelog = "https://github.com/firetail-io/finchly-sdk-py/blob/main/CHANGELOG.md"
27
+
28
+ [project.optional-dependencies]
29
+ dev = ["pytest>=8", "build>=1.2"]
30
+
31
+ [tool.hatch.version]
32
+ path = "src/firetail_finchly/_version.py"
33
+
34
+ [tool.hatch.build.targets.wheel]
35
+ packages = ["src/firetail_finchly"]
36
+
37
+ [tool.pytest.ini_options]
38
+ testpaths = ["tests"]
@@ -0,0 +1,37 @@
1
+ """Python SDK for the Finchly platform."""
2
+
3
+ from ._version import __version__
4
+ from .config import DEFAULT_MANIFEST_PATH, PlatformConfig, load_config
5
+ from .errors import IDENTITY_REJECTIONS, FinchlyError, IdentityError
6
+ from .identity import IDENTITY_HEADER, Identity, IdentityVerifier
7
+ from .manifest import check_manifest, load_manifest
8
+ from .platform_api import (
9
+ MANIFEST_VERSION,
10
+ PLATFORM_API_VERSION,
11
+ SDK_NAME,
12
+ SDK_VERSION,
13
+ SUPPORTED_PLATFORM_API_VERSIONS,
14
+ )
15
+ from .service import ServiceClient, ServiceResponse
16
+
17
+ __all__ = [
18
+ "DEFAULT_MANIFEST_PATH",
19
+ "FinchlyError",
20
+ "IDENTITY_HEADER",
21
+ "IDENTITY_REJECTIONS",
22
+ "Identity",
23
+ "IdentityError",
24
+ "IdentityVerifier",
25
+ "MANIFEST_VERSION",
26
+ "PLATFORM_API_VERSION",
27
+ "PlatformConfig",
28
+ "SDK_NAME",
29
+ "SDK_VERSION",
30
+ "SUPPORTED_PLATFORM_API_VERSIONS",
31
+ "ServiceClient",
32
+ "ServiceResponse",
33
+ "__version__",
34
+ "check_manifest",
35
+ "load_config",
36
+ "load_manifest",
37
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "0.0.1"