allowly 0.2.0__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,45 @@
1
+ name: publish
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ permissions:
8
+ contents: read
9
+ id-token: write
10
+
11
+ jobs:
12
+ publish:
13
+ name: Publish Python SDK
14
+ runs-on: ubuntu-latest
15
+ environment:
16
+ name: pypi
17
+ url: https://pypi.org/p/allowly
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ with:
21
+ fetch-depth: 0
22
+ - name: Require tag commit on main
23
+ run: |
24
+ git fetch origin main
25
+ git merge-base --is-ancestor "$GITHUB_SHA" origin/main
26
+ - uses: actions/setup-python@v5
27
+ with:
28
+ python-version: "3.12"
29
+ - name: Install package and build tools
30
+ run: python -m pip install ".[dev]" build twine
31
+ - run: pytest -q
32
+ - name: Check tag matches package version
33
+ run: |
34
+ python - <<'PY'
35
+ import os
36
+ import tomllib
37
+
38
+ with open("pyproject.toml", "rb") as file:
39
+ version = tomllib.load(file)["project"]["version"]
40
+ assert os.environ["GITHUB_REF_NAME"] == f"v{version}"
41
+ PY
42
+ - run: python -m build
43
+ - run: python -m twine check dist/*
44
+ - name: Publish to PyPI
45
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,24 @@
1
+ name: verify
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ verify:
13
+ name: Verify Python SDK
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: "3.12"
20
+ - name: Install package and build tools
21
+ run: python -m pip install ".[dev]" build twine
22
+ - run: pytest -q
23
+ - run: python -m build
24
+ - run: python -m twine check dist/*
@@ -0,0 +1,9 @@
1
+ .venv/
2
+ .DS_Store
3
+ .pytest_cache/
4
+ __pycache__/
5
+ *.py[cod]
6
+ .coverage
7
+ dist/
8
+ build/
9
+ *.egg-info/
allowly-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Allowly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
allowly-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.4
2
+ Name: allowly
3
+ Version: 0.2.0
4
+ Summary: Python SDK for the Allowly API
5
+ Project-URL: Repository, https://github.com/Allowly-AI/allowly-sdk-python
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.10
9
+ Requires-Dist: httpx>=0.27.0
10
+ Provides-Extra: dev
11
+ Requires-Dist: allowly-receipt-format<4.0.0,>=3.0.0; extra == 'dev'
12
+ Requires-Dist: cryptography>=42; extra == 'dev'
13
+ Requires-Dist: fastmcp<3,>=2.9; extra == 'dev'
14
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
15
+ Requires-Dist: pytest>=8.0; extra == 'dev'
16
+ Requires-Dist: respx>=0.21; extra == 'dev'
17
+ Provides-Extra: fastmcp
18
+ Requires-Dist: fastmcp<3,>=2.9; extra == 'fastmcp'
19
+ Provides-Extra: verifier
20
+ Requires-Dist: allowly-receipt-format<4.0.0,>=3.0.0; extra == 'verifier'
21
+ Description-Content-Type: text/markdown
22
+
23
+ # Allowly Python SDK
24
+
25
+ Async Python client for the Allowly runtime API.
26
+
27
+ MCP middleware ships inside this SDK: `pip install 'allowly[fastmcp]'`, then `from allowly.mcp import AllowlyMCPMiddleware`. In TypeScript, it lives in the separate `@allowly/mcp` package.
28
+
29
+ ## Subject authorization pattern
30
+
31
+ Do not send raw user/customer PII to Allowly receipts unless you intentionally
32
+ want it in your audit trail. Create one authorization per subject, store the
33
+ returned authorization ID in your own app database, and use that ID for later checks.
34
+
35
+ ```python
36
+ import asyncio
37
+ import os
38
+
39
+ from allowly import Allowly
40
+
41
+
42
+ async def main() -> None:
43
+ async with Allowly(
44
+ api_key=os.environ["ALLOWLY_API_KEY"],
45
+ base_url=os.getenv("ALLOWLY_API_URL", "https://api.allowly.ai"),
46
+ ) as allowly:
47
+ # Your app creates a stable internal subject ID.
48
+ subject_id = "subject_abc123"
49
+
50
+ # Store this in your app table, for example:
51
+ # allowly_authorizations(subject_id, policy_id, allowly_authorization_id, status)
52
+ authorization = await allowly.authorizations.create(
53
+ user_id=f"subject:{subject_id}",
54
+ policy_id="research_agent",
55
+ metadata={"source": "import"},
56
+ )
57
+
58
+ # Before the agent acts, check whether this action is allowed.
59
+ decision = await allowly.check(
60
+ authorization_id=authorization.authorization_id,
61
+ actions=["web.search"],
62
+ resource=f"subject:{subject_id}",
63
+ context={"stage": "research"},
64
+ )
65
+
66
+ if decision.results["web.search"].decision != "allow":
67
+ raise RuntimeError("Action is not authorized")
68
+
69
+
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ Local development against the documented Caddy endpoint requires the edge
74
+ token that Cloudflare injects for public traffic. Pass it explicitly:
75
+
76
+ ```python
77
+ Allowly(
78
+ api_key=os.environ["ALLOWLY_API_KEY"],
79
+ base_url="http://localhost:8443",
80
+ dangerously_allow_insecure_base_url=True,
81
+ edge_token=os.environ["ALLOWLY_EDGE_TOKEN"],
82
+ )
83
+ ```
84
+
85
+ The token is only sent when provided; never set it for the public API.
86
+
87
+ Inline authorization creation requires `agent_id`, `actions`, and `expires_at`.
88
+ Policy-based creation uses `policy_id` instead and rejects inline action or
89
+ decision-override fields.
90
+
91
+ Unavailable checks fail closed unless an action is explicitly mapped to
92
+ `"fail_open"` with `fallback_by_action`. Unmapped actions always fail closed.
93
+
94
+ For actions that need third-party approval, define the escalation rule on the
95
+ agent policy, create the authorization from that `policy_id`, and then resolve
96
+ returned escalation results with
97
+ `await allowly.escalations.approve(escalation_id, resolved_by="manager:123")`
98
+ or `reject(...)`, then re-check before running the action.
99
+
100
+ If you need lookup by email later, import `from_email` from
101
+ `allowly.identifiers` and store `from_email(email, pepper=APP_PII_PEPPER)`.
102
+ The helper trims and lowercases only, prefixes the result with `email_hmac:v1`,
103
+ and never sends the raw email or pepper to Allowly. Keep the pepper stable and
104
+ backed up; changing it changes derived user IDs. Keep raw names, emails,
105
+ documents, and profile URLs out of Allowly receipts unless those fields are
106
+ intentionally part of your audit record.
107
+
108
+ Do not add raw HTTP fallbacks in application code for APIs the SDK is missing.
109
+ Patch this SDK first, then use the typed client from the app. That keeps the
110
+ integration examples honest and makes SDK gaps visible early.
111
+
112
+ ## Offline receipt verification
113
+
114
+ Install `allowly[verifier]` to verify signed receipts locally. The extra uses
115
+ `allowly-receipt-format>=3.0.0,<4.0.0`, which verifies receipt wire format 3 (the package major equals the wire format). `alg` and
116
+ `key_id` are signed top-level fields, and `signature` is the base64url string.
117
+
118
+ Key-document fetching requires HTTPS by default. For the documented local
119
+ Caddy endpoint only, pass `dangerously_allow_insecure_base_url=True` and its
120
+ `edge_token` to `fetch_keys_doc`, matching the client options above.
@@ -0,0 +1,98 @@
1
+ # Allowly Python SDK
2
+
3
+ Async Python client for the Allowly runtime API.
4
+
5
+ MCP middleware ships inside this SDK: `pip install 'allowly[fastmcp]'`, then `from allowly.mcp import AllowlyMCPMiddleware`. In TypeScript, it lives in the separate `@allowly/mcp` package.
6
+
7
+ ## Subject authorization pattern
8
+
9
+ Do not send raw user/customer PII to Allowly receipts unless you intentionally
10
+ want it in your audit trail. Create one authorization per subject, store the
11
+ returned authorization ID in your own app database, and use that ID for later checks.
12
+
13
+ ```python
14
+ import asyncio
15
+ import os
16
+
17
+ from allowly import Allowly
18
+
19
+
20
+ async def main() -> None:
21
+ async with Allowly(
22
+ api_key=os.environ["ALLOWLY_API_KEY"],
23
+ base_url=os.getenv("ALLOWLY_API_URL", "https://api.allowly.ai"),
24
+ ) as allowly:
25
+ # Your app creates a stable internal subject ID.
26
+ subject_id = "subject_abc123"
27
+
28
+ # Store this in your app table, for example:
29
+ # allowly_authorizations(subject_id, policy_id, allowly_authorization_id, status)
30
+ authorization = await allowly.authorizations.create(
31
+ user_id=f"subject:{subject_id}",
32
+ policy_id="research_agent",
33
+ metadata={"source": "import"},
34
+ )
35
+
36
+ # Before the agent acts, check whether this action is allowed.
37
+ decision = await allowly.check(
38
+ authorization_id=authorization.authorization_id,
39
+ actions=["web.search"],
40
+ resource=f"subject:{subject_id}",
41
+ context={"stage": "research"},
42
+ )
43
+
44
+ if decision.results["web.search"].decision != "allow":
45
+ raise RuntimeError("Action is not authorized")
46
+
47
+
48
+ asyncio.run(main())
49
+ ```
50
+
51
+ Local development against the documented Caddy endpoint requires the edge
52
+ token that Cloudflare injects for public traffic. Pass it explicitly:
53
+
54
+ ```python
55
+ Allowly(
56
+ api_key=os.environ["ALLOWLY_API_KEY"],
57
+ base_url="http://localhost:8443",
58
+ dangerously_allow_insecure_base_url=True,
59
+ edge_token=os.environ["ALLOWLY_EDGE_TOKEN"],
60
+ )
61
+ ```
62
+
63
+ The token is only sent when provided; never set it for the public API.
64
+
65
+ Inline authorization creation requires `agent_id`, `actions`, and `expires_at`.
66
+ Policy-based creation uses `policy_id` instead and rejects inline action or
67
+ decision-override fields.
68
+
69
+ Unavailable checks fail closed unless an action is explicitly mapped to
70
+ `"fail_open"` with `fallback_by_action`. Unmapped actions always fail closed.
71
+
72
+ For actions that need third-party approval, define the escalation rule on the
73
+ agent policy, create the authorization from that `policy_id`, and then resolve
74
+ returned escalation results with
75
+ `await allowly.escalations.approve(escalation_id, resolved_by="manager:123")`
76
+ or `reject(...)`, then re-check before running the action.
77
+
78
+ If you need lookup by email later, import `from_email` from
79
+ `allowly.identifiers` and store `from_email(email, pepper=APP_PII_PEPPER)`.
80
+ The helper trims and lowercases only, prefixes the result with `email_hmac:v1`,
81
+ and never sends the raw email or pepper to Allowly. Keep the pepper stable and
82
+ backed up; changing it changes derived user IDs. Keep raw names, emails,
83
+ documents, and profile URLs out of Allowly receipts unless those fields are
84
+ intentionally part of your audit record.
85
+
86
+ Do not add raw HTTP fallbacks in application code for APIs the SDK is missing.
87
+ Patch this SDK first, then use the typed client from the app. That keeps the
88
+ integration examples honest and makes SDK gaps visible early.
89
+
90
+ ## Offline receipt verification
91
+
92
+ Install `allowly[verifier]` to verify signed receipts locally. The extra uses
93
+ `allowly-receipt-format>=3.0.0,<4.0.0`, which verifies receipt wire format 3 (the package major equals the wire format). `alg` and
94
+ `key_id` are signed top-level fields, and `signature` is the base64url string.
95
+
96
+ Key-document fetching requires HTTPS by default. For the documented local
97
+ Caddy endpoint only, pass `dangerously_allow_insecure_base_url=True` and its
98
+ `edge_token` to `fetch_keys_doc`, matching the client options above.
@@ -0,0 +1,55 @@
1
+ from __future__ import annotations
2
+
3
+ from .client import Allowly
4
+ from .error import AllowlyAPIError, AllowlyProtocolError, FieldError
5
+ from .types import (
6
+ ActionCheckResult,
7
+ ActionCheckResultAllow,
8
+ ActionCheckResultConfirm,
9
+ ActionCheckResultDeny,
10
+ ActionCheckResultEscalate,
11
+ ActionEntry,
12
+ CheckResponse,
13
+ ConfirmationApproveResponse,
14
+ AuthorizationCreateResponse,
15
+ AuthorizationRevokeResponse,
16
+ BudgetInfo,
17
+ BudgetSettlementResponse,
18
+ Decision,
19
+ EscalationInfo,
20
+ EscalationResolveResponse,
21
+ FallbackMode,
22
+ PolicyConditionEvidence,
23
+ PolicyEvalInfo,
24
+ ReceiptEnvelope,
25
+ ReceiptEnvelopePending,
26
+ ReceiptEnvelopeSigned,
27
+ )
28
+
29
+ __all__ = [
30
+ "Allowly",
31
+ "AllowlyAPIError",
32
+ "AllowlyProtocolError",
33
+ "FieldError",
34
+ "ActionCheckResult",
35
+ "ActionCheckResultAllow",
36
+ "ActionCheckResultConfirm",
37
+ "ActionCheckResultDeny",
38
+ "ActionCheckResultEscalate",
39
+ "ActionEntry",
40
+ "CheckResponse",
41
+ "BudgetInfo",
42
+ "BudgetSettlementResponse",
43
+ "EscalationInfo",
44
+ "EscalationResolveResponse",
45
+ "AuthorizationCreateResponse",
46
+ "AuthorizationRevokeResponse",
47
+ "ConfirmationApproveResponse",
48
+ "PolicyConditionEvidence",
49
+ "PolicyEvalInfo",
50
+ "ReceiptEnvelope",
51
+ "ReceiptEnvelopePending",
52
+ "ReceiptEnvelopeSigned",
53
+ "Decision",
54
+ "FallbackMode",
55
+ ]