sfdctrust 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 (45) hide show
  1. sfdctrust-0.1.0/LICENSE +21 -0
  2. sfdctrust-0.1.0/PKG-INFO +208 -0
  3. sfdctrust-0.1.0/README.md +173 -0
  4. sfdctrust-0.1.0/docs/API_CONTRACT.md +402 -0
  5. sfdctrust-0.1.0/docs/CHANGELOG.md +63 -0
  6. sfdctrust-0.1.0/poetry.lock +2033 -0
  7. sfdctrust-0.1.0/pyproject.toml +119 -0
  8. sfdctrust-0.1.0/src/sfdctrust/__init__.py +54 -0
  9. sfdctrust-0.1.0/src/sfdctrust/_async_client.py +132 -0
  10. sfdctrust-0.1.0/src/sfdctrust/_async_pat_refresh.py +151 -0
  11. sfdctrust-0.1.0/src/sfdctrust/_async_transport.py +129 -0
  12. sfdctrust-0.1.0/src/sfdctrust/_auth.py +96 -0
  13. sfdctrust-0.1.0/src/sfdctrust/_client.py +126 -0
  14. sfdctrust-0.1.0/src/sfdctrust/_constants.py +14 -0
  15. sfdctrust-0.1.0/src/sfdctrust/_exceptions.py +131 -0
  16. sfdctrust-0.1.0/src/sfdctrust/_pat_refresh.py +239 -0
  17. sfdctrust-0.1.0/src/sfdctrust/_pat_store.py +99 -0
  18. sfdctrust-0.1.0/src/sfdctrust/_transport.py +326 -0
  19. sfdctrust-0.1.0/src/sfdctrust/_version.py +84 -0
  20. sfdctrust-0.1.0/src/sfdctrust/integrations/__init__.py +8 -0
  21. sfdctrust-0.1.0/src/sfdctrust/models/__init__.py +8 -0
  22. sfdctrust-0.1.0/src/sfdctrust/models/_wire.py +39 -0
  23. sfdctrust-0.1.0/src/sfdctrust/models/events.py +228 -0
  24. sfdctrust-0.1.0/src/sfdctrust/models/status.py +33 -0
  25. sfdctrust-0.1.0/src/sfdctrust/models/tenants.py +74 -0
  26. sfdctrust-0.1.0/src/sfdctrust/py.typed +0 -0
  27. sfdctrust-0.1.0/src/sfdctrust/resources/__init__.py +8 -0
  28. sfdctrust-0.1.0/src/sfdctrust/resources/_async_events.py +154 -0
  29. sfdctrust-0.1.0/src/sfdctrust/resources/_async_status.py +29 -0
  30. sfdctrust-0.1.0/src/sfdctrust/resources/_async_tenants.py +94 -0
  31. sfdctrust-0.1.0/src/sfdctrust/resources/_requests.py +134 -0
  32. sfdctrust-0.1.0/src/sfdctrust/resources/events.py +228 -0
  33. sfdctrust-0.1.0/src/sfdctrust/resources/status.py +39 -0
  34. sfdctrust-0.1.0/src/sfdctrust/resources/tenants.py +115 -0
  35. sfdctrust-0.1.0/tests/fixtures/.gitkeep +0 -0
  36. sfdctrust-0.1.0/tests/unit/test_async_client.py +428 -0
  37. sfdctrust-0.1.0/tests/unit/test_async_pat_refresh.py +440 -0
  38. sfdctrust-0.1.0/tests/unit/test_auth.py +194 -0
  39. sfdctrust-0.1.0/tests/unit/test_client_retries.py +146 -0
  40. sfdctrust-0.1.0/tests/unit/test_exceptions.py +102 -0
  41. sfdctrust-0.1.0/tests/unit/test_pat_refresh.py +492 -0
  42. sfdctrust-0.1.0/tests/unit/test_read_resources.py +563 -0
  43. sfdctrust-0.1.0/tests/unit/test_tenants.py +241 -0
  44. sfdctrust-0.1.0/tests/unit/test_transport.py +336 -0
  45. sfdctrust-0.1.0/tests/unit/test_version.py +62 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jeff Shurtliff
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,208 @@
1
+ Metadata-Version: 2.4
2
+ Name: sfdctrust
3
+ Version: 0.1.0
4
+ Summary: Unofficial Python SDK for the Salesforce My Trust Center API
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Keywords: salesforce,sfdc,trust,uptime,api,rest,tooling,python
8
+ Author: Jeff Shurtliff
9
+ Author-email: jeff@shurt.us
10
+ Maintainer: Jeff Shurtliff
11
+ Maintainer-email: jeff@shurt.us
12
+ Requires-Python: >=3.12
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Information Technology
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Environment :: Web Environment
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Topic :: Internet :: WWW/HTTP
20
+ Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
21
+ Classifier: Topic :: Software Development :: Libraries
22
+ Classifier: Topic :: System :: Systems Administration
23
+ Classifier: Programming Language :: Python :: 3
24
+ Classifier: Programming Language :: Python :: 3 :: Only
25
+ Classifier: Programming Language :: Python :: 3.12
26
+ Classifier: Programming Language :: Python :: 3.13
27
+ Classifier: Programming Language :: Python :: 3.14
28
+ Classifier: Programming Language :: Python :: 3.15
29
+ Requires-Dist: httpx (>=0.28.1,<1.0.0)
30
+ Requires-Dist: pydantic (>=2.14.2,<3.0.0)
31
+ Project-URL: Issues, https://github.com/jeffshurtliff/sfdctrust/issues
32
+ Project-URL: Repository, https://github.com/jeffshurtliff/sfdctrust
33
+ Description-Content-Type: text/markdown
34
+
35
+ <p align="center">
36
+ <picture>
37
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jeffshurtliff/sfdctrust/main/docs/_static/logos-dark/sfdctrust-logo-dark-320.png">
38
+ <img src="https://raw.githubusercontent.com/jeffshurtliff/sfdctrust/main/docs/_static/logos/sfdctrust-logo-320.png" alt="sfdctrust logo" width="200">
39
+ </picture>
40
+ </p>
41
+
42
+ # sfdctrust
43
+
44
+ An unofficial, community-maintained Python SDK project for the **Salesforce My Trust Center
45
+ API**. It is not affiliated with or supported by Salesforce.
46
+
47
+ The synchronous and asynchronous clients list authorized tenants, retrieve
48
+ status snapshots, search events, and return event timelines and tenant history
49
+ through their detail operations.
50
+
51
+ The API exposes only tenants authorized by the caller's personal access token (PAT);
52
+ empty results do not establish Salesforce-wide health.
53
+
54
+ See the [API contract and implementation decisions](https://sfdctrust.readthedocs.io/en/latest/API_CONTRACT.html) for the verified
55
+ operation inventory, schema notes, and authentication rules.
56
+
57
+ ## Quickstart
58
+
59
+ Install from PyPI with `python -m pip install sfdctrust` on Python 3.12 or later.
60
+ Set `SFDCTRUST_PAT` in the process environment using a PAT from My Trust Center.
61
+ For installation in a virtual environment, see [Installation](https://sfdctrust.readthedocs.io/en/latest/getting-started/installation.html).
62
+ For creating a PAT and entering it at a hidden prompt, see
63
+ [Prepare to use sfdctrust](https://sfdctrust.readthedocs.io/en/latest/getting-started/setup.html).
64
+
65
+ ```python
66
+ from sfdctrust import TrustCenterClient
67
+
68
+ with TrustCenterClient.from_env() as client:
69
+ for tenant in client.tenants.iter_all(page_size=50):
70
+ print(tenant.id, tenant.name)
71
+ ```
72
+
73
+ If you use `client.tenants.list()` to fetch a single page and `page.next_page_token`
74
+ is set, request the next page with
75
+ `client.tenants.list(page_token=page.next_page_token)` inside the same context.
76
+ If you supplied `page_size` or `locale` on the first call, repeat those values.
77
+ See the [quickstart](https://sfdctrust.readthedocs.io/en/latest/getting-started/quickstart.html) and
78
+ [tenant reference](https://sfdctrust.readthedocs.io/en/latest/reference/tenants.html) for details.
79
+
80
+ ## Use the asynchronous client
81
+
82
+ ```python
83
+ import asyncio
84
+
85
+ from sfdctrust import AsyncTrustCenterClient
86
+
87
+
88
+ async def main():
89
+ async with AsyncTrustCenterClient.from_env() as client:
90
+ snapshot = await client.status.snapshot()
91
+ print('Ongoing incidents', len(snapshot.ongoing.incident))
92
+ async for tenant in client.tenants.iter_all(page_size=50):
93
+ print(tenant.id, tenant.name)
94
+
95
+
96
+ if __name__ == '__main__':
97
+ asyncio.run(main())
98
+ ```
99
+
100
+ Await single-page methods and the `list_all()` and `search_all()` collectors.
101
+ Consume `iter_all()` and `iter_search()` directly with `async for`.
102
+ Both clients use the same arguments, response models, and retry rules.
103
+ [Package reference](https://sfdctrust.readthedocs.io/en/latest/reference/package.html)
104
+
105
+ ## Read status, search events, and inspect details
106
+
107
+ Use IDs from the tenant list, snapshot, or search results. Tenant detail
108
+ includes Salesforce's event history window. Event detail includes the
109
+ timeline when entries are available.
110
+
111
+ ```python
112
+ from datetime import UTC, datetime, timedelta
113
+
114
+ from sfdctrust import TrustCenterClient
115
+ from sfdctrust.models.events import EventSearchScope
116
+
117
+ with TrustCenterClient.from_env() as client:
118
+ snapshot = client.status.snapshot(locale='en-US')
119
+ for incident in snapshot.ongoing.incident:
120
+ print(incident.event_id, incident.title, list(incident.scope.tenants))
121
+
122
+ tenants = client.tenants.list()
123
+ if tenants.tenants:
124
+ tenant_id = tenants.tenants[0].id
125
+ end = datetime.now(UTC)
126
+ results = client.events.search_all(
127
+ scopes=[EventSearchScope(id=tenant_id)],
128
+ start_time=end - timedelta(days=30),
129
+ end_time=end,
130
+ event_types=['incident'],
131
+ page_size=50,
132
+ locale='en-US',
133
+ )
134
+
135
+ if results:
136
+ event = client.events.get(results[0].event_id, locale='en-US')
137
+ for entry in event.timeline or []:
138
+ print(entry.created_at, entry.sub_event_type, entry.message)
139
+
140
+ tenant = client.tenants.get(tenant_id, locale='en-US')
141
+ for event in tenant.events:
142
+ print(event.event_id, event.status)
143
+ ```
144
+
145
+ Use `client.tenants.list_all(page_size=50)` to collect tenants, or use
146
+ `client.events.iter_search(...)` with the same search arguments to consume
147
+ events lazily. Both traversals preserve order and duplicates. An empty page
148
+ continues when a cursor is present, including an empty string. A repeated cursor
149
+ or a required page beyond `max_pages` (default 1000) raises `PaginationError`
150
+ from `sfdctrust`. Iterators can yield items before a failure. Stale cursors raise
151
+ HTTP 400 `APIStatusError` without a restart.
152
+
153
+ All credential entry points default to two additional attempts per eligible read.
154
+ Set `max_retries=0` to disable SDK retries or choose an integer up to 5.
155
+
156
+ The search includes the start instant and excludes the end instant. Each
157
+ continuation repeats the original parameters and uses the token from the
158
+ preceding page. Salesforce's tenant history covers lifecycle overlap
159
+ within 365 days before or after the request time. Informational messages
160
+ cover the preceding 365 days. The PAT determines which tenants and events
161
+ are visible. An empty snapshot bucket, search page, or history establishes
162
+ no Salesforce-wide health claim.
163
+
164
+ See the [status and event reference](https://sfdctrust.readthedocs.io/en/latest/reference/events-and-status.html) for signatures and models.
165
+
166
+ ## Rotate a PAT explicitly
167
+
168
+ For rotation, use `TrustCenterClient.from_store(store)` with a seeded,
169
+ caller-supplied adapter that provides durable atomic updates shared by all
170
+ rotators of the PAT. Call `client.refresh_pat(expires_in_days=7)` to save and
171
+ activate a replacement. It sends one POST and returns a secret-free
172
+ `PATRefreshResult`. The constructor and `from_env()` remain static credential
173
+ modes. See the [authentication reference](https://sfdctrust.readthedocs.io/en/latest/reference/authentication.html)
174
+ for the adapter contract and recovery runbook. To create and seed a working
175
+ local test store, follow [Prepare to use sfdctrust](https://sfdctrust.readthedocs.io/en/latest/getting-started/setup.html).
176
+
177
+ For asyncio, use `await AsyncTrustCenterClient.from_store(store)` with an
178
+ `AsyncPATStore`, then await rotation and any persistence recovery.
179
+ [Async adapter contract and cancellation recovery](https://sfdctrust.readthedocs.io/en/latest/reference/authentication.html#async-store-backed-clients)
180
+
181
+ ## Development
182
+
183
+ Requires Python **3.12 or later** and Poetry **2.2 or later, below 3**.
184
+ From this checkout:
185
+
186
+ ```sh
187
+ poetry install
188
+ poetry run pytest -q
189
+ poetry run ruff check .
190
+ poetry run ruff format --check .
191
+ poetry build
192
+ ```
193
+
194
+ The SDK uses HTTPX and Pydantic v2. Tests run offline with synthetic PATs.
195
+
196
+ The [exception hierarchy](https://sfdctrust.readthedocs.io/en/latest/reference/exceptions.html) is available from `sfdctrust`, including
197
+ `ConfigurationError`, `AuthenticationError`, and the shared `TrustCenterError` base.
198
+ See [authentication](https://sfdctrust.readthedocs.io/en/latest/reference/authentication.html) for the implemented boundaries.
199
+
200
+ The documentation uses Sphinx with the optional `docs` dependency group:
201
+
202
+ ```sh
203
+ poetry install --with docs
204
+ poetry run sphinx-build -W --keep-going -E -a -b html docs docs/_build/html
205
+ ```
206
+
207
+ See [Maintain the documentation](https://sfdctrust.readthedocs.io/en/latest/maintainers/documentation.html) for details.
208
+
@@ -0,0 +1,173 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jeffshurtliff/sfdctrust/main/docs/_static/logos-dark/sfdctrust-logo-dark-320.png">
4
+ <img src="https://raw.githubusercontent.com/jeffshurtliff/sfdctrust/main/docs/_static/logos/sfdctrust-logo-320.png" alt="sfdctrust logo" width="200">
5
+ </picture>
6
+ </p>
7
+
8
+ # sfdctrust
9
+
10
+ An unofficial, community-maintained Python SDK project for the **Salesforce My Trust Center
11
+ API**. It is not affiliated with or supported by Salesforce.
12
+
13
+ The synchronous and asynchronous clients list authorized tenants, retrieve
14
+ status snapshots, search events, and return event timelines and tenant history
15
+ through their detail operations.
16
+
17
+ The API exposes only tenants authorized by the caller's personal access token (PAT);
18
+ empty results do not establish Salesforce-wide health.
19
+
20
+ See the [API contract and implementation decisions](https://sfdctrust.readthedocs.io/en/latest/API_CONTRACT.html) for the verified
21
+ operation inventory, schema notes, and authentication rules.
22
+
23
+ ## Quickstart
24
+
25
+ Install from PyPI with `python -m pip install sfdctrust` on Python 3.12 or later.
26
+ Set `SFDCTRUST_PAT` in the process environment using a PAT from My Trust Center.
27
+ For installation in a virtual environment, see [Installation](https://sfdctrust.readthedocs.io/en/latest/getting-started/installation.html).
28
+ For creating a PAT and entering it at a hidden prompt, see
29
+ [Prepare to use sfdctrust](https://sfdctrust.readthedocs.io/en/latest/getting-started/setup.html).
30
+
31
+ ```python
32
+ from sfdctrust import TrustCenterClient
33
+
34
+ with TrustCenterClient.from_env() as client:
35
+ for tenant in client.tenants.iter_all(page_size=50):
36
+ print(tenant.id, tenant.name)
37
+ ```
38
+
39
+ If you use `client.tenants.list()` to fetch a single page and `page.next_page_token`
40
+ is set, request the next page with
41
+ `client.tenants.list(page_token=page.next_page_token)` inside the same context.
42
+ If you supplied `page_size` or `locale` on the first call, repeat those values.
43
+ See the [quickstart](https://sfdctrust.readthedocs.io/en/latest/getting-started/quickstart.html) and
44
+ [tenant reference](https://sfdctrust.readthedocs.io/en/latest/reference/tenants.html) for details.
45
+
46
+ ## Use the asynchronous client
47
+
48
+ ```python
49
+ import asyncio
50
+
51
+ from sfdctrust import AsyncTrustCenterClient
52
+
53
+
54
+ async def main():
55
+ async with AsyncTrustCenterClient.from_env() as client:
56
+ snapshot = await client.status.snapshot()
57
+ print('Ongoing incidents', len(snapshot.ongoing.incident))
58
+ async for tenant in client.tenants.iter_all(page_size=50):
59
+ print(tenant.id, tenant.name)
60
+
61
+
62
+ if __name__ == '__main__':
63
+ asyncio.run(main())
64
+ ```
65
+
66
+ Await single-page methods and the `list_all()` and `search_all()` collectors.
67
+ Consume `iter_all()` and `iter_search()` directly with `async for`.
68
+ Both clients use the same arguments, response models, and retry rules.
69
+ [Package reference](https://sfdctrust.readthedocs.io/en/latest/reference/package.html)
70
+
71
+ ## Read status, search events, and inspect details
72
+
73
+ Use IDs from the tenant list, snapshot, or search results. Tenant detail
74
+ includes Salesforce's event history window. Event detail includes the
75
+ timeline when entries are available.
76
+
77
+ ```python
78
+ from datetime import UTC, datetime, timedelta
79
+
80
+ from sfdctrust import TrustCenterClient
81
+ from sfdctrust.models.events import EventSearchScope
82
+
83
+ with TrustCenterClient.from_env() as client:
84
+ snapshot = client.status.snapshot(locale='en-US')
85
+ for incident in snapshot.ongoing.incident:
86
+ print(incident.event_id, incident.title, list(incident.scope.tenants))
87
+
88
+ tenants = client.tenants.list()
89
+ if tenants.tenants:
90
+ tenant_id = tenants.tenants[0].id
91
+ end = datetime.now(UTC)
92
+ results = client.events.search_all(
93
+ scopes=[EventSearchScope(id=tenant_id)],
94
+ start_time=end - timedelta(days=30),
95
+ end_time=end,
96
+ event_types=['incident'],
97
+ page_size=50,
98
+ locale='en-US',
99
+ )
100
+
101
+ if results:
102
+ event = client.events.get(results[0].event_id, locale='en-US')
103
+ for entry in event.timeline or []:
104
+ print(entry.created_at, entry.sub_event_type, entry.message)
105
+
106
+ tenant = client.tenants.get(tenant_id, locale='en-US')
107
+ for event in tenant.events:
108
+ print(event.event_id, event.status)
109
+ ```
110
+
111
+ Use `client.tenants.list_all(page_size=50)` to collect tenants, or use
112
+ `client.events.iter_search(...)` with the same search arguments to consume
113
+ events lazily. Both traversals preserve order and duplicates. An empty page
114
+ continues when a cursor is present, including an empty string. A repeated cursor
115
+ or a required page beyond `max_pages` (default 1000) raises `PaginationError`
116
+ from `sfdctrust`. Iterators can yield items before a failure. Stale cursors raise
117
+ HTTP 400 `APIStatusError` without a restart.
118
+
119
+ All credential entry points default to two additional attempts per eligible read.
120
+ Set `max_retries=0` to disable SDK retries or choose an integer up to 5.
121
+
122
+ The search includes the start instant and excludes the end instant. Each
123
+ continuation repeats the original parameters and uses the token from the
124
+ preceding page. Salesforce's tenant history covers lifecycle overlap
125
+ within 365 days before or after the request time. Informational messages
126
+ cover the preceding 365 days. The PAT determines which tenants and events
127
+ are visible. An empty snapshot bucket, search page, or history establishes
128
+ no Salesforce-wide health claim.
129
+
130
+ See the [status and event reference](https://sfdctrust.readthedocs.io/en/latest/reference/events-and-status.html) for signatures and models.
131
+
132
+ ## Rotate a PAT explicitly
133
+
134
+ For rotation, use `TrustCenterClient.from_store(store)` with a seeded,
135
+ caller-supplied adapter that provides durable atomic updates shared by all
136
+ rotators of the PAT. Call `client.refresh_pat(expires_in_days=7)` to save and
137
+ activate a replacement. It sends one POST and returns a secret-free
138
+ `PATRefreshResult`. The constructor and `from_env()` remain static credential
139
+ modes. See the [authentication reference](https://sfdctrust.readthedocs.io/en/latest/reference/authentication.html)
140
+ for the adapter contract and recovery runbook. To create and seed a working
141
+ local test store, follow [Prepare to use sfdctrust](https://sfdctrust.readthedocs.io/en/latest/getting-started/setup.html).
142
+
143
+ For asyncio, use `await AsyncTrustCenterClient.from_store(store)` with an
144
+ `AsyncPATStore`, then await rotation and any persistence recovery.
145
+ [Async adapter contract and cancellation recovery](https://sfdctrust.readthedocs.io/en/latest/reference/authentication.html#async-store-backed-clients)
146
+
147
+ ## Development
148
+
149
+ Requires Python **3.12 or later** and Poetry **2.2 or later, below 3**.
150
+ From this checkout:
151
+
152
+ ```sh
153
+ poetry install
154
+ poetry run pytest -q
155
+ poetry run ruff check .
156
+ poetry run ruff format --check .
157
+ poetry build
158
+ ```
159
+
160
+ The SDK uses HTTPX and Pydantic v2. Tests run offline with synthetic PATs.
161
+
162
+ The [exception hierarchy](https://sfdctrust.readthedocs.io/en/latest/reference/exceptions.html) is available from `sfdctrust`, including
163
+ `ConfigurationError`, `AuthenticationError`, and the shared `TrustCenterError` base.
164
+ See [authentication](https://sfdctrust.readthedocs.io/en/latest/reference/authentication.html) for the implemented boundaries.
165
+
166
+ The documentation uses Sphinx with the optional `docs` dependency group:
167
+
168
+ ```sh
169
+ poetry install --with docs
170
+ poetry run sphinx-build -W --keep-going -E -a -b html docs docs/_build/html
171
+ ```
172
+
173
+ See [Maintain the documentation](https://sfdctrust.readthedocs.io/en/latest/maintainers/documentation.html) for details.