sealkeeper 0.1.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.
Files changed (42) hide show
  1. sealkeeper-0.1.0/.gitignore +6 -0
  2. sealkeeper-0.1.0/LICENSE +21 -0
  3. sealkeeper-0.1.0/PKG-INFO +162 -0
  4. sealkeeper-0.1.0/README.md +142 -0
  5. sealkeeper-0.1.0/pyproject.toml +55 -0
  6. sealkeeper-0.1.0/src/sealkeeper/__init__.py +37 -0
  7. sealkeeper-0.1.0/src/sealkeeper/_protocol.py +277 -0
  8. sealkeeper-0.1.0/src/sealkeeper/_version.py +1 -0
  9. sealkeeper-0.1.0/src/sealkeeper/client.py +303 -0
  10. sealkeeper-0.1.0/src/sealkeeper/errors.py +23 -0
  11. sealkeeper-0.1.0/src/sealkeeper/gate.py +259 -0
  12. sealkeeper-0.1.0/src/sealkeeper/lookup.py +120 -0
  13. sealkeeper-0.1.0/src/sealkeeper/py.typed +0 -0
  14. sealkeeper-0.1.0/src/sealkeeper/seal.py +339 -0
  15. sealkeeper-0.1.0/tests/conftest.py +7 -0
  16. sealkeeper-0.1.0/tests/fixtures/agent.json +7 -0
  17. sealkeeper-0.1.0/tests/fixtures/index.json +786 -0
  18. sealkeeper-0.1.0/tests/fixtures/keys.json +11 -0
  19. sealkeeper-0.1.0/tests/fixtures/seals/v1-bad-signature.txt +1 -0
  20. sealkeeper-0.1.0/tests/fixtures/seals/v1-expired.txt +1 -0
  21. sealkeeper-0.1.0/tests/fixtures/seals/v1-unknown-kid.txt +1 -0
  22. sealkeeper-0.1.0/tests/fixtures/seals/v1-valid.txt +1 -0
  23. sealkeeper-0.1.0/tests/fixtures/seals/v4-valid.txt +1 -0
  24. sealkeeper-0.1.0/tests/fixtures/seals/v6-valid.txt +1 -0
  25. sealkeeper-0.1.0/tests/fixtures/seals/v7-listed-x402-valid.txt +1 -0
  26. sealkeeper-0.1.0/tests/fixtures/seals/v7-service-valid.txt +1 -0
  27. sealkeeper-0.1.0/tests/fixtures/seals/v7-valid.txt +1 -0
  28. sealkeeper-0.1.0/tests/recorded/get-trust-is-error.json +19 -0
  29. sealkeeper-0.1.0/tests/recorded/get-trust-not-found.json +28 -0
  30. sealkeeper-0.1.0/tests/recorded/get-trust-shown.json +42 -0
  31. sealkeeper-0.1.0/tests/recorded/keys-production.json +18 -0
  32. sealkeeper-0.1.0/tests/recorded/query-400.json +19 -0
  33. sealkeeper-0.1.0/tests/recorded/unknown-tool.json +14 -0
  34. sealkeeper-0.1.0/tests/support.py +230 -0
  35. sealkeeper-0.1.0/tests/test_did.py +84 -0
  36. sealkeeper-0.1.0/tests/test_fixtures.py +56 -0
  37. sealkeeper-0.1.0/tests/test_gate.py +433 -0
  38. sealkeeper-0.1.0/tests/test_keys.py +158 -0
  39. sealkeeper-0.1.0/tests/test_live.py +37 -0
  40. sealkeeper-0.1.0/tests/test_lookup.py +219 -0
  41. sealkeeper-0.1.0/tests/test_seal.py +283 -0
  42. sealkeeper-0.1.0/tests/test_transport.py +267 -0
@@ -0,0 +1,6 @@
1
+ .venv/
2
+ __pycache__/
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ dist/
6
+ uv.lock
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 The SealKeeper Authors
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.
@@ -0,0 +1,162 @@
1
+ Metadata-Version: 2.5
2
+ Name: sealkeeper
3
+ Version: 0.1.0
4
+ Summary: Look up and verify SealKeeper SEALs, and gate agent and service calls on them.
5
+ Project-URL: Homepage, https://sealkeeper.run
6
+ Project-URL: SEAL Standard, https://sealkeeper.run/seal/standard
7
+ Project-URL: Repository, https://github.com/sealkeeper-dev/sealkeeper-py
8
+ Project-URL: Issues, https://github.com/sealkeeper-dev/sealkeeper-py/issues
9
+ Author: The SealKeeper Authors
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Typing :: Typed
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: cryptography>=42
18
+ Requires-Dist: httpx<1,>=0.27
19
+ Description-Content-Type: text/markdown
20
+
21
+ # sealkeeper
22
+
23
+ Look up an AI agent or service on [SealKeeper](https://sealkeeper.run), verify its SEAL on your own machine and decide whether to call it.
24
+
25
+ A SEAL is SealKeeper's signed credential for an agent or a service. This library checks it against SealKeeper's published keys, so what you act on is what SealKeeper signed.
26
+
27
+ ## Install
28
+
29
+ ```sh
30
+ pip install sealkeeper
31
+ ```
32
+
33
+ Python 3.10 or later. It depends on `httpx` and `cryptography` only.
34
+
35
+ ## Look up
36
+
37
+ ```python
38
+ from sealkeeper import SealKeeper
39
+
40
+ with SealKeeper() as sk:
41
+ found = sk.lookup("alice/helper")
42
+
43
+ if found.verified:
44
+ print(found.kind, found.level, found.trust)
45
+ else:
46
+ print(found.code)
47
+ ```
48
+
49
+ A target is any of these.
50
+
51
+ - A handle, such as `alice/helper`.
52
+ - A page URL, `https://sealkeeper.run/agents/alice/helper` or `https://sealkeeper.run/agents/<agent id>`.
53
+ - An agent id, 43 characters of base64url, or its `did:key`.
54
+ - A card URL, or an MCP server's URL. Pass it as it is listed, since the host is the only part read case blind.
55
+
56
+ `lookup` returns a `Lookup`. `seal` is the verified SEAL, and `kind`, `level` and `trust` read from it, so they are `None` unless it verified. Without one, `code` says why.
57
+
58
+ | `code` | Meaning |
59
+ |---|---|
60
+ | `broken` | The SEAL did not verify. `broken` holds the reason |
61
+ | `withheld` | SealKeeper withholds the SEAL. `reason` and `since` say why and from when |
62
+ | `no_seal` | No SEAL while the agent is dormant. `dormant_days` says how long |
63
+ | `checking` | SealKeeper is still checking it |
64
+ | `delisted` | SealKeeper no longer lists it |
65
+ | `not_found` | The target names no agent or service |
66
+ | `unknown` | No SEAL, and the answer says nothing more |
67
+
68
+ A code this version does not know comes through as sent, with the whole answer in `raw`. `handle` is the handle SealKeeper answered, for display only.
69
+
70
+ ## Verify
71
+
72
+ ```python
73
+ from sealkeeper import SealBroken, SealKeeper
74
+
75
+ with SealKeeper() as sk:
76
+ try:
77
+ seal = sk.verify(seal_text, expected_agent_id)
78
+ except SealBroken as broken:
79
+ print(broken.reason)
80
+ ```
81
+
82
+ `expected_agent_id` is the agent you are about to trust, as an agent id or a `did:key`. A valid SEAL for another agent is `wrong_agent`.
83
+
84
+ `verify` runs the checks of the [SEAL Standard](https://sealkeeper.run/seal/standard) in order. The header, the key its `kid` names, the signature, the issuer, the version, the shape, `exp`, `iat` and `sub`. The reasons are in `SEAL_BROKEN_REASONS`.
85
+
86
+ - `malformed`, `unknown_kid`, `bad_signature`, `wrong_issuer`, `unsupported_version`, `expired`, `not_yet_valid`, `wrong_agent`
87
+
88
+ It returns a `VerifiedSeal` with `agent_id`, `kind` (`agent` or `service`), `level` (`none`, `bronze`, `silver` or `gold`), `trust` (the Trust Score, `None` before version 4), `issued_at`, `expires_at` and `payload`.
89
+
90
+ The keys come from `https://sealkeeper.run/.well-known/seal.json`. A copy is kept for five minutes and fetched again for a `kid` it does not know. When no copy younger than five minutes can be had, `verify` raises `UnavailableError` and never trusts an older one.
91
+
92
+ The shape check covers the fields this library reads and returns, `sub`, `iat`, `exp`, `level`, `trust`, `subject.kind` and `listing.card_url`. It is not a full conformance reader of the standard, so read the rest of `payload` with that in mind.
93
+
94
+ With a key document of your own, `verify_with_keys(seal, read_keys(document), now, expected_agent_id)` does the same offline.
95
+
96
+ `verify_seal(seal, agent=None, at=None)` asks SealKeeper to check a SEAL instead, and answers as SealKeeper does, with its record of holds.
97
+
98
+ ## Gate
99
+
100
+ ```python
101
+ from sealkeeper import Gate
102
+
103
+ gate = Gate()
104
+ decision = gate.check("https://mcp.example.com/mcp", "service")
105
+ if decision.passed:
106
+ call_it()
107
+ else:
108
+ print(decision.code, decision.reason)
109
+ ```
110
+
111
+ `check(target, kind)` answers `Pass` or `Block`, each with `passed` and `code`. A pass on a SEAL is `sealed`, with the `VerifiedSeal` in `seal`. `kind` is `"agent"` or `"service"`. `acheck` is the same, awaited. Neither raises for a decision, so a check never ends your agent's run.
112
+
113
+ A pass needs a current, valid SEAL of that kind at bronze or above. For a card or MCP server URL, the SEAL must also name that URL.
114
+
115
+ - `Gate(allow_unsealed=True)` also passes a target SealKeeper does not know, one it is still checking and one with no SEAL while dormant. `Pass.code` then says which, and `Pass.seal` is `None`. Everything else still blocks.
116
+ - `cache_ttl` keeps each lookup for that many seconds, 300 by default, 0 to 86,400. A failed lookup is never kept, and a kept SEAL is checked against its `exp` on every use.
117
+
118
+ Each decision writes one INFO line to the `sealkeeper` logger, with the code, the kind and the agent's handle, or the host of a URL. Never the SEAL or a full URL.
119
+
120
+ ### Block codes
121
+
122
+ `BLOCK_CODES` holds every one.
123
+
124
+ | Code | Meaning |
125
+ |---|---|
126
+ | `unavailable` | SealKeeper could not be reached, timed out or asked to slow down |
127
+ | `bad_target` | SealKeeper refused the target. `reason` is its line |
128
+ | `error` | SealKeeper answered something this library cannot read |
129
+ | `not_found` | The target names no agent or service |
130
+ | `checking` | SealKeeper is still checking it |
131
+ | `no_seal` | No SEAL while it is dormant |
132
+ | `withheld` | SealKeeper withholds its SEAL. `reason` names why |
133
+ | `delisted` | SealKeeper no longer lists it |
134
+ | `unknown` | No SEAL, and nothing more said |
135
+ | `wrong_url` | The SEAL is for another URL than the target |
136
+ | `wrong_kind` | The SEAL is for the other kind |
137
+ | `below_bronze` | The SEAL's level is none |
138
+ | `malformed` and the other seven reasons of `SEAL_BROKEN_REASONS` | The SEAL did not verify |
139
+
140
+ ## Errors
141
+
142
+ `lookup`, `get_trust`, `verify_seal` and `verify` raise these, all `SealKeeperError`.
143
+
144
+ - `BadTargetError`, SealKeeper refused the target, with its line.
145
+ - `UnavailableError`, SealKeeper could not be reached in 5 seconds or kept answering 429.
146
+ - `SealBroken`, from `verify`, with `reason`.
147
+ - `SealKeeperError` itself, for an answer this library cannot read.
148
+
149
+ ## Options
150
+
151
+ Both `SealKeeper` and `AsyncSealKeeper` take these, all optional.
152
+
153
+ - `http_client`, an `httpx.Client` or `httpx.AsyncClient` to send through.
154
+ - `clock`, a function answering Unix seconds, for the SEAL checks.
155
+ - `user_agent_suffix`, added to the `User-Agent`, such as `"my-wrapper/1.0.0"`.
156
+ - `sleep`, what waits before the one retry of a 429.
157
+
158
+ `Gate` takes `client`, `async_client` and `clock` the same way.
159
+
160
+ ## License
161
+
162
+ MIT
@@ -0,0 +1,142 @@
1
+ # sealkeeper
2
+
3
+ Look up an AI agent or service on [SealKeeper](https://sealkeeper.run), verify its SEAL on your own machine and decide whether to call it.
4
+
5
+ A SEAL is SealKeeper's signed credential for an agent or a service. This library checks it against SealKeeper's published keys, so what you act on is what SealKeeper signed.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ pip install sealkeeper
11
+ ```
12
+
13
+ Python 3.10 or later. It depends on `httpx` and `cryptography` only.
14
+
15
+ ## Look up
16
+
17
+ ```python
18
+ from sealkeeper import SealKeeper
19
+
20
+ with SealKeeper() as sk:
21
+ found = sk.lookup("alice/helper")
22
+
23
+ if found.verified:
24
+ print(found.kind, found.level, found.trust)
25
+ else:
26
+ print(found.code)
27
+ ```
28
+
29
+ A target is any of these.
30
+
31
+ - A handle, such as `alice/helper`.
32
+ - A page URL, `https://sealkeeper.run/agents/alice/helper` or `https://sealkeeper.run/agents/<agent id>`.
33
+ - An agent id, 43 characters of base64url, or its `did:key`.
34
+ - A card URL, or an MCP server's URL. Pass it as it is listed, since the host is the only part read case blind.
35
+
36
+ `lookup` returns a `Lookup`. `seal` is the verified SEAL, and `kind`, `level` and `trust` read from it, so they are `None` unless it verified. Without one, `code` says why.
37
+
38
+ | `code` | Meaning |
39
+ |---|---|
40
+ | `broken` | The SEAL did not verify. `broken` holds the reason |
41
+ | `withheld` | SealKeeper withholds the SEAL. `reason` and `since` say why and from when |
42
+ | `no_seal` | No SEAL while the agent is dormant. `dormant_days` says how long |
43
+ | `checking` | SealKeeper is still checking it |
44
+ | `delisted` | SealKeeper no longer lists it |
45
+ | `not_found` | The target names no agent or service |
46
+ | `unknown` | No SEAL, and the answer says nothing more |
47
+
48
+ A code this version does not know comes through as sent, with the whole answer in `raw`. `handle` is the handle SealKeeper answered, for display only.
49
+
50
+ ## Verify
51
+
52
+ ```python
53
+ from sealkeeper import SealBroken, SealKeeper
54
+
55
+ with SealKeeper() as sk:
56
+ try:
57
+ seal = sk.verify(seal_text, expected_agent_id)
58
+ except SealBroken as broken:
59
+ print(broken.reason)
60
+ ```
61
+
62
+ `expected_agent_id` is the agent you are about to trust, as an agent id or a `did:key`. A valid SEAL for another agent is `wrong_agent`.
63
+
64
+ `verify` runs the checks of the [SEAL Standard](https://sealkeeper.run/seal/standard) in order. The header, the key its `kid` names, the signature, the issuer, the version, the shape, `exp`, `iat` and `sub`. The reasons are in `SEAL_BROKEN_REASONS`.
65
+
66
+ - `malformed`, `unknown_kid`, `bad_signature`, `wrong_issuer`, `unsupported_version`, `expired`, `not_yet_valid`, `wrong_agent`
67
+
68
+ It returns a `VerifiedSeal` with `agent_id`, `kind` (`agent` or `service`), `level` (`none`, `bronze`, `silver` or `gold`), `trust` (the Trust Score, `None` before version 4), `issued_at`, `expires_at` and `payload`.
69
+
70
+ The keys come from `https://sealkeeper.run/.well-known/seal.json`. A copy is kept for five minutes and fetched again for a `kid` it does not know. When no copy younger than five minutes can be had, `verify` raises `UnavailableError` and never trusts an older one.
71
+
72
+ The shape check covers the fields this library reads and returns, `sub`, `iat`, `exp`, `level`, `trust`, `subject.kind` and `listing.card_url`. It is not a full conformance reader of the standard, so read the rest of `payload` with that in mind.
73
+
74
+ With a key document of your own, `verify_with_keys(seal, read_keys(document), now, expected_agent_id)` does the same offline.
75
+
76
+ `verify_seal(seal, agent=None, at=None)` asks SealKeeper to check a SEAL instead, and answers as SealKeeper does, with its record of holds.
77
+
78
+ ## Gate
79
+
80
+ ```python
81
+ from sealkeeper import Gate
82
+
83
+ gate = Gate()
84
+ decision = gate.check("https://mcp.example.com/mcp", "service")
85
+ if decision.passed:
86
+ call_it()
87
+ else:
88
+ print(decision.code, decision.reason)
89
+ ```
90
+
91
+ `check(target, kind)` answers `Pass` or `Block`, each with `passed` and `code`. A pass on a SEAL is `sealed`, with the `VerifiedSeal` in `seal`. `kind` is `"agent"` or `"service"`. `acheck` is the same, awaited. Neither raises for a decision, so a check never ends your agent's run.
92
+
93
+ A pass needs a current, valid SEAL of that kind at bronze or above. For a card or MCP server URL, the SEAL must also name that URL.
94
+
95
+ - `Gate(allow_unsealed=True)` also passes a target SealKeeper does not know, one it is still checking and one with no SEAL while dormant. `Pass.code` then says which, and `Pass.seal` is `None`. Everything else still blocks.
96
+ - `cache_ttl` keeps each lookup for that many seconds, 300 by default, 0 to 86,400. A failed lookup is never kept, and a kept SEAL is checked against its `exp` on every use.
97
+
98
+ Each decision writes one INFO line to the `sealkeeper` logger, with the code, the kind and the agent's handle, or the host of a URL. Never the SEAL or a full URL.
99
+
100
+ ### Block codes
101
+
102
+ `BLOCK_CODES` holds every one.
103
+
104
+ | Code | Meaning |
105
+ |---|---|
106
+ | `unavailable` | SealKeeper could not be reached, timed out or asked to slow down |
107
+ | `bad_target` | SealKeeper refused the target. `reason` is its line |
108
+ | `error` | SealKeeper answered something this library cannot read |
109
+ | `not_found` | The target names no agent or service |
110
+ | `checking` | SealKeeper is still checking it |
111
+ | `no_seal` | No SEAL while it is dormant |
112
+ | `withheld` | SealKeeper withholds its SEAL. `reason` names why |
113
+ | `delisted` | SealKeeper no longer lists it |
114
+ | `unknown` | No SEAL, and nothing more said |
115
+ | `wrong_url` | The SEAL is for another URL than the target |
116
+ | `wrong_kind` | The SEAL is for the other kind |
117
+ | `below_bronze` | The SEAL's level is none |
118
+ | `malformed` and the other seven reasons of `SEAL_BROKEN_REASONS` | The SEAL did not verify |
119
+
120
+ ## Errors
121
+
122
+ `lookup`, `get_trust`, `verify_seal` and `verify` raise these, all `SealKeeperError`.
123
+
124
+ - `BadTargetError`, SealKeeper refused the target, with its line.
125
+ - `UnavailableError`, SealKeeper could not be reached in 5 seconds or kept answering 429.
126
+ - `SealBroken`, from `verify`, with `reason`.
127
+ - `SealKeeperError` itself, for an answer this library cannot read.
128
+
129
+ ## Options
130
+
131
+ Both `SealKeeper` and `AsyncSealKeeper` take these, all optional.
132
+
133
+ - `http_client`, an `httpx.Client` or `httpx.AsyncClient` to send through.
134
+ - `clock`, a function answering Unix seconds, for the SEAL checks.
135
+ - `user_agent_suffix`, added to the `User-Agent`, such as `"my-wrapper/1.0.0"`.
136
+ - `sleep`, what waits before the one retry of a 429.
137
+
138
+ `Gate` takes `client`, `async_client` and `clock` the same way.
139
+
140
+ ## License
141
+
142
+ MIT
@@ -0,0 +1,55 @@
1
+ [project]
2
+ name = "sealkeeper"
3
+ dynamic = ["version"]
4
+ description = "Look up and verify SealKeeper SEALs, and gate agent and service calls on them."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ authors = [{ name = "The SealKeeper Authors" }]
9
+ requires-python = ">=3.10"
10
+ dependencies = ["httpx>=0.27,<1", "cryptography>=42"]
11
+ classifiers = [
12
+ "Programming Language :: Python :: 3",
13
+ "Programming Language :: Python :: 3.10",
14
+ "Programming Language :: Python :: 3.14",
15
+ "Typing :: Typed",
16
+ ]
17
+
18
+ [project.urls]
19
+ Homepage = "https://sealkeeper.run"
20
+ "SEAL Standard" = "https://sealkeeper.run/seal/standard"
21
+ Repository = "https://github.com/sealkeeper-dev/sealkeeper-py"
22
+ Issues = "https://github.com/sealkeeper-dev/sealkeeper-py/issues"
23
+
24
+ [build-system]
25
+ requires = ["hatchling>=1.27"]
26
+ build-backend = "hatchling.build"
27
+
28
+ [tool.hatch.version]
29
+ path = "src/sealkeeper/_version.py"
30
+
31
+ [tool.hatch.build.targets.wheel]
32
+ packages = ["src/sealkeeper"]
33
+
34
+ [tool.hatch.build.targets.sdist]
35
+ only-include = ["src/sealkeeper", "tests", "README.md", "LICENSE"]
36
+
37
+ [dependency-groups]
38
+ dev = ["pytest>=8.3", "ruff>=0.14"]
39
+
40
+ [tool.ruff]
41
+ target-version = "py310"
42
+ line-length = 88
43
+ include = ["*.py"]
44
+
45
+ [tool.ruff.lint]
46
+ select = ["E", "F", "W", "I", "UP", "B", "SIM", "RUF"]
47
+
48
+ [tool.ruff.lint.isort]
49
+ known-first-party = ["sealkeeper", "support"]
50
+
51
+ [tool.pytest.ini_options]
52
+ testpaths = ["tests"]
53
+ # Async tests run on anyio. pytest-asyncio stays off where it is installed.
54
+ addopts = "--strict-markers -p no:asyncio"
55
+ markers = ["live: calls production, runs only with SEALKEEPER_LIVE=1"]
@@ -0,0 +1,37 @@
1
+ """Look up and verify SealKeeper SEALs, and gate calls on them."""
2
+
3
+ from ._version import __version__
4
+ from .client import AsyncSealKeeper, SealKeeper
5
+ from .errors import BadTargetError, SealBroken, SealKeeperError, UnavailableError
6
+ from .gate import BLOCK_CODES, PASS_CODES, Block, Gate, Pass
7
+ from .lookup import Lookup
8
+ from .seal import (
9
+ SEAL_BROKEN_REASONS,
10
+ SEAL_VERSIONS,
11
+ VerifiedSeal,
12
+ agent_id_from_did_key,
13
+ read_keys,
14
+ verify_with_keys,
15
+ )
16
+
17
+ __all__ = [
18
+ "BLOCK_CODES",
19
+ "PASS_CODES",
20
+ "SEAL_BROKEN_REASONS",
21
+ "SEAL_VERSIONS",
22
+ "AsyncSealKeeper",
23
+ "BadTargetError",
24
+ "Block",
25
+ "Gate",
26
+ "Lookup",
27
+ "Pass",
28
+ "SealBroken",
29
+ "SealKeeper",
30
+ "SealKeeperError",
31
+ "UnavailableError",
32
+ "VerifiedSeal",
33
+ "__version__",
34
+ "agent_id_from_did_key",
35
+ "read_keys",
36
+ "verify_with_keys",
37
+ ]