dmn-sdk 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.
dmn_sdk-0.1.0/LICENSE ADDED
@@ -0,0 +1,158 @@
1
+ # DMN Mesh — Restricted-Use Evaluation Licence
2
+
3
+ **Issuer:** Tata Communications Limited ("**TCL**")
4
+ **Software:** dmn-ucpe and the DMN Mesh agent, dashboard, SDK and associated artefacts (collectively, the "**Software**")
5
+ **Classification:** Internal — Restricted Evaluation Release
6
+ **Status:** This is a **pre-release / testing build**. It is not a production-grade product.
7
+
8
+ > **NOTICE — DRAFT FOR LEGAL REVIEW.** This text is a working draft authored for an internal evaluation programme. It must be reviewed and signed off by TCL Legal before being presented to any tenant for acceptance.
9
+
10
+ ---
11
+
12
+ ## 1. Acceptance
13
+
14
+ 1.1. By logging in to the DMN Mesh dashboard or invoking the DMN Mesh APIs as a tenant operator, the legal entity on whose behalf the tenant was provisioned (the "**Tenant**") is presented with this Licence and must indicate acceptance before any tenant-scoped feature becomes available.
15
+
16
+ 1.2. Acceptance is recorded as an immutable, hash-chained audit entry signed by the issuing DMN agent, including the OpenID Connect (OIDC) identity of the human operator who acted, the software version, and the cryptographic digest of the Licence text shown at the time of acceptance.
17
+
18
+ 1.3. **Acceptance is required again whenever a new release of the Software is installed on the mesh, or whenever the text of this Licence changes.** Previously recorded acceptances do not carry forward across release boundaries.
19
+
20
+ 1.4. The person clicking "Accept" warrants that they are duly authorised to bind the Tenant to this Licence.
21
+
22
+ ## 2. Limited Evaluation Grant
23
+
24
+ 2.1. Subject to the Tenant's continued compliance with this Licence, TCL grants the Tenant a **non-exclusive, non-transferable, non-sublicensable, revocable** licence to access and use the Software **solely within the DMN mesh instance operated by TCL** for the Tenant's **internal evaluation and operational testing** purposes.
25
+
26
+ 2.2. The grant is limited to use through the named tenant identity provisioned by TCL. The Tenant has no right to use the Software outside that tenant scope, on behalf of any third party, or for any commercial purpose other than the Tenant's own internal evaluation.
27
+
28
+ 2.3. No title, ownership or intellectual property right in the Software is transferred to the Tenant. All rights not expressly granted are reserved by TCL.
29
+
30
+ ## 3. Restrictions — Anti-Copying and Anti-Redistribution
31
+
32
+ The Tenant **shall not, and shall not permit any person under its control or instruction to**:
33
+
34
+ 3.1. **Copy, mirror, archive, snapshot, image or export** the Software (in binary, source, container, image, package, configuration, key-material or any other form) outside the TCL-operated DMN mesh.
35
+
36
+ 3.2. **Install, deploy, run, register, attach, join or attempt to onboard** the Software onto any node, device, hardware, virtual machine, container, cloud account, partner network or third-party environment that has not been explicitly authorised in writing by TCL.
37
+
38
+ 3.3. **Redistribute, sublicense, lease, lend, sell, rent, host as a service, or otherwise make the Software available** to any third party.
39
+
40
+ 3.4. **Reverse engineer, decompile, disassemble, decrypt, extract, or attempt to derive the source code, structure, internal logic, keying material, or trade secrets** of the Software, except to the minimum extent expressly required by applicable law and only after notifying TCL in writing.
41
+
42
+ 3.5. **Remove, obscure, alter, or interfere with** any proprietary notice, copyright marker, licence banner, watermark, audit hook, attestation envelope, signature envelope, or telemetry channel embedded in the Software.
43
+
44
+ 3.6. **Publish or disclose** any benchmark, performance number, comparison, screenshot, defect report, security finding, training output, derived dataset, or analytical result obtained through the Software, in any external venue, without TCL's prior written consent.
45
+
46
+ 3.7. **Share, delegate, federate or transfer** the tenant identity, the OIDC credentials, the cryptographic keys, the signed manifests, or any session token issued by the Software, to any person other than employees of the Tenant who are individually bound by confidentiality obligations no less protective than those in this Licence.
47
+
48
+ 3.8. **Use the Software to circumvent or attempt to circumvent** any access control, policy gate, capability gate, attestation check, signature check, audit log, tenant scoping, or rate limit imposed by the Software or by TCL.
49
+
50
+ ## 4. Pre-Release Software Notice and Risk Acknowledgement
51
+
52
+ 4.1. **The Software is a pre-release, experimental, testing build.** It is provided for the Tenant's evaluation only and is **not warranted, certified, or intended for production, mission-critical, regulated, safety-of-life, or revenue-generating workloads**.
53
+
54
+ 4.2. **The operational impact of the Software on the Tenant's existing systems, networks, applications, security posture, regulatory compliance, data integrity, or service availability has not been measured, validated, or certified** by TCL. The Tenant acknowledges that the Software may interact with the Tenant's environment in ways that are not fully characterised.
55
+
56
+ 4.3. **The Software is expected to contain defects, regressions, breaking changes, behavioural surprises, performance variability, and undocumented behaviour.** Frequent releases — including releases that alter, remove, or replace capabilities the Tenant currently relies on — are anticipated.
57
+
58
+ 4.4. **The Tenant agrees to use the Software with the utmost caution and responsibility**, including but not limited to: deploying it first in non-critical environments, maintaining independent backups and rollback paths, isolating it from systems whose continued operation is materially important, and treating every release as a fresh evaluation event.
59
+
60
+ 4.5. The Tenant acknowledges that this Licence and the Software may be modified, suspended, withdrawn or terminated by TCL at any time, with or without notice, including in response to operational risk.
61
+
62
+ ## 5. Tenant Responsibilities — Monitoring, Review, and Reporting
63
+
64
+ The Tenant shall, on a continuing basis and at the Tenant's sole cost:
65
+
66
+ 5.1. **Continuously monitor** the behaviour, output, resource consumption, network activity, and security posture of the Software within the Tenant's environment.
67
+
68
+ 5.2. **Periodically review** the Software's effects on the Tenant's connected systems, including any unexpected change in performance, latency, traffic patterns, data flows, model outputs, or third-party integrations.
69
+
70
+ 5.3. **Promptly report** to TCL, through the channel TCL designates, any of the following: suspected defect; suspected security vulnerability; suspected data leakage; suspected policy violation; suspected unauthorised access; unexpected behaviour of any kind; any third-party complaint, dispute, regulatory enquiry, or incident response engagement that relates in any way to the Software.
71
+
72
+ 5.4. **Cooperate in good faith** with any TCL investigation, including by providing relevant logs, audit chain entries, configuration, and signed manifests on TCL's reasonable request.
73
+
74
+ 5.5. **Not delay** reporting under section 5.3 for commercial, reputational, or convenience reasons. The Tenant acknowledges that silence on a known defect or vulnerability is itself a material breach of this Licence.
75
+
76
+ ## 6. Disclaimer of Warranties
77
+
78
+ 6.1. **THE SOFTWARE IS PROVIDED "AS IS" AND "AS AVAILABLE", WITH ALL FAULTS AND WITHOUT WARRANTY OF ANY KIND.** To the maximum extent permitted by applicable law, TCL expressly disclaims all warranties, conditions, representations and terms — whether express, implied, statutory, contractual, customary, or otherwise — including without limitation any warranty of **merchantability, fitness for a particular purpose, accuracy, completeness, reliability, availability, timeliness, security, non-infringement, quiet enjoyment, title, or course of dealing**.
79
+
80
+ 6.2. **TCL does not warrant** that the Software will meet the Tenant's requirements, operate without interruption, be free of defects, be free of vulnerabilities, produce correct results, preserve any data, prevent unauthorised access, comply with any standard, integrate with any third-party product, or be compatible with any future version of itself.
81
+
82
+ 6.3. **No advice or information**, whether oral or written, obtained from TCL or through the Software, shall create any warranty not expressly stated in this Licence.
83
+
84
+ ## 7. Limitation of Liability and Indemnity
85
+
86
+ 7.1. **TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, TCL SHALL NOT BE LIABLE TO THE TENANT, ITS AFFILIATES, ITS PERSONNEL, ITS CUSTOMERS, OR ANY THIRD PARTY** for any direct, indirect, incidental, special, consequential, exemplary, punitive, or any other damages whatsoever arising out of, related to, or in connection with the Software, this Licence, or the Tenant's use of, inability to use, or reliance on the Software — including without limitation damages for loss of profits, loss of revenue, loss of business, loss of goodwill, loss of data, loss of use, loss of customers, cost of substitute products, regulatory fines, third-party claims, downtime, breach of security, breach of confidentiality, or any other commercial or operational loss — even if TCL has been advised of, or could have foreseen, the possibility of such damages.
87
+
88
+ 7.2. The Tenant acknowledges that **the aggregate liability of TCL** under or in connection with this Licence and the Software, on any theory of liability whatsoever (including contract, tort, strict liability, statute, or otherwise), shall **not exceed zero (₹0)**, the parties having expressly priced the Software as a no-fee evaluation release.
89
+
90
+ 7.3. **The Tenant shall defend, indemnify, and hold harmless** TCL, its affiliates, and their respective officers, directors, employees, and agents from and against any and all third-party claims, demands, actions, proceedings, damages, liabilities, losses, costs and expenses (including reasonable legal fees) arising out of or in connection with: (a) the Tenant's breach of this Licence; (b) the Tenant's use of the Software in violation of section 3; (c) the Tenant's failure to monitor, review, or report as required by section 5; or (d) any data, content or input the Tenant introduces into the Software.
91
+
92
+ 7.4. The exclusions and limitations in this section 7 apply regardless of whether the remedies in this Licence are found to have failed of their essential purpose. They form an essential basis of the bargain between the parties; absent them, TCL would not provide the Software on a no-fee evaluation basis.
93
+
94
+ ## 8. Audit, Logging and Telemetry
95
+
96
+ 8.1. The Tenant acknowledges and consents that the Software records, and that TCL may collect and retain, **a hash-chained audit log of all Tenant-initiated state changes**, including without limitation: acceptance of this Licence, app and API onboarding, pipeline operations, model promotion, round abort, tenant withdrawal, and any operator action through the dashboard.
97
+
98
+ 8.2. Audit entries are append-only, signed, and chained by cryptographic hash. The Tenant shall not delete, alter, redact, exfiltrate, or interfere with the audit log.
99
+
100
+ 8.3. TCL may use audit data to investigate suspected breach of this Licence, to respond to incidents, to improve the Software, and to satisfy regulatory or contractual obligations.
101
+
102
+ ## 9. Suspension and Termination
103
+
104
+ 9.1. TCL may **suspend or terminate** this Licence, the Tenant's access to the Software, or any specific capability, at any time, with or without notice, for any reason or no reason, including suspected breach of section 3 (Restrictions), section 5 (Reporting), or section 8 (Audit).
105
+
106
+ 9.2. The Tenant may terminate this Licence by ceasing all use of the Software and requesting tenant withdrawal through the dashboard.
107
+
108
+ 9.3. **Effect of tenant deletion.** Upon withdrawal or deletion of the Tenant from the mesh — whether by the Tenant, by TCL, or automatically as a consequence of suspension — this Licence is automatically terminated, the Tenant's acceptance record is tombstoned, and the agreement becomes **null and void** as between the parties on a go-forward basis. Sections 3, 5.3, 6, 7, 8 and 11 survive termination.
109
+
110
+ 9.4. Termination does not entitle the Tenant to a refund or any other remedy; the Software is provided at no charge.
111
+
112
+ ## 10. No Support, No Service Level
113
+
114
+ 10.1. TCL is under **no obligation** to provide support, bug fixes, updates, upgrades, patches, security advisories, migrations, or professional services in connection with the Software.
115
+
116
+ 10.2. No service-level agreement, availability commitment, or response-time commitment of any kind applies to the Software.
117
+
118
+ ## 11. Confidentiality
119
+
120
+ 11.1. The Software, its design, its internal interfaces, the contents of any audit or telemetry data shared with the Tenant, and any defect or security finding the Tenant becomes aware of through use of the Software, are **TCL Confidential Information**.
121
+
122
+ 11.2. The Tenant shall protect TCL Confidential Information with at least the same degree of care it uses for its own most sensitive confidential information, and in no event less than a reasonable degree of care.
123
+
124
+ ## 12. Compliance with Law
125
+
126
+ 12.1. The Tenant shall use the Software in compliance with all applicable laws and regulations, including without limitation laws governing data protection, privacy, cryptography, export control, sanctions, and information security.
127
+
128
+ 12.2. The Software contains cryptographic components. The Tenant is solely responsible for ensuring that its use, possession, import or transmission of such components is lawful in every jurisdiction in which the Tenant operates.
129
+
130
+ ## 13. Governing Law and Jurisdiction
131
+
132
+ 13.1. This Licence is governed by and construed in accordance with the laws of the Republic of India, without regard to its conflict-of-laws principles.
133
+
134
+ 13.2. The courts at **Mumbai, Maharashtra, India** shall have exclusive jurisdiction over any dispute arising out of or in connection with this Licence.
135
+
136
+ ## 14. Entire Agreement; Severability; No Waiver
137
+
138
+ 14.1. This Licence is the entire agreement between the parties with respect to the Software and supersedes any prior or contemporaneous agreement, representation, or understanding.
139
+
140
+ 14.2. If any provision of this Licence is held to be unenforceable, the remaining provisions shall continue in full force and effect, and the unenforceable provision shall be replaced by an enforceable provision that most closely reflects the parties' intent.
141
+
142
+ 14.3. No failure or delay by TCL in exercising any right under this Licence shall operate as a waiver of that right.
143
+
144
+ ## 15. Acceptance Mechanism
145
+
146
+ 15.1. Acceptance is captured electronically by the DMN Mesh dashboard, signed by the issuing agent, and stored as an immutable, hash-chained audit entry. Each acceptance entry binds together:
147
+
148
+ - the canonical name of the Tenant on the mesh;
149
+ - the software version and the SHA-256 digest of this Licence text;
150
+ - the OIDC issuer, subject, and bearer-token digest of the human operator who acted;
151
+ - the hybrid logical clock and wall-clock timestamps of the action; and
152
+ - the identity of the agent that witnessed the acceptance.
153
+
154
+ 15.2. The Tenant agrees that this electronic record constitutes valid, enforceable evidence of acceptance, equivalent to a handwritten signature.
155
+
156
+ ---
157
+
158
+ *End of Licence.* © Tata Communications Limited. All rights reserved.
dmn_sdk-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,237 @@
1
+ Metadata-Version: 2.4
2
+ Name: dmn-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the DMN mesh — call tenant APIs from your code.
5
+ Author-email: inno8cube <dmn-dev@inno8cube.com>
6
+ License-Expression: LicenseRef-RestrictedUseEvaluation
7
+ Project-URL: Homepage, https://github.com/dmn-ucpe/dmn-ucpe
8
+ Project-URL: Documentation, https://github.com/dmn-ucpe/dmn-ucpe/blob/main/docs/SDK.md
9
+ Project-URL: Source, https://github.com/dmn-ucpe/dmn-ucpe/tree/main/sdk/python
10
+ Keywords: dmn,mesh,ndn,sdk,iot,telemetry
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: requests<3,>=2.31
23
+ Requires-Dist: websocket-client<2,>=1.7
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=7.0; extra == "dev"
26
+ Requires-Dist: responses>=0.23; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ # dmn-sdk
30
+
31
+ Python client for the DMN mesh. Call tenant APIs from your own code with one import.
32
+
33
+ ## Install
34
+
35
+ From a checkout:
36
+
37
+ ```bash
38
+ pip install ./sdk/python/
39
+ ```
40
+
41
+ From PyPI (v0.282.3+):
42
+
43
+ ```bash
44
+ pip install dmn-sdk
45
+ ```
46
+
47
+ Requires Python 3.9+. Pulls in `requests` and `websocket-client`.
48
+
49
+ ## Quickstart
50
+
51
+ ```python
52
+ import dmn_sdk as dmn
53
+
54
+ client = dmn.connect() # reads DMN_API_TOKEN env
55
+ ingestor = client.tenant("/tata/dmn/acme").app("ingestor")
56
+
57
+ # Unary POST
58
+ resp = ingestor.post("/telemetry", json={"sensor": "s1", "value": 23.4})
59
+ print(resp.status_code, resp.json())
60
+
61
+ # Subscribe to a topic
62
+ for msg in ingestor.subscribe("telemetry"):
63
+ print(msg.seq, msg.payload)
64
+ ```
65
+
66
+ ## Authentication
67
+
68
+ Three sources, in priority order:
69
+
70
+ 1. **Explicit kwargs** to `dmn.connect(token=..., base_url=...)`
71
+ 2. **Environment**: `DMN_API_TOKEN` + `DMN_API_BASE_URL` + (optional) `DMN_PROFILE`
72
+ 3. **Config file** `~/.dmn/credentials` (INI-style)
73
+
74
+ ### Env var setup
75
+
76
+ ```bash
77
+ # Mint a token at /ui/dev/tokens/ on the dashboard,
78
+ # then export:
79
+ export DMN_API_TOKEN="eyJhbGc...the wire JWT..."
80
+ export DMN_API_BASE_URL="https://console.dmn.inno8cube.com"
81
+ ```
82
+
83
+ ### Config file setup
84
+
85
+ `~/.dmn/credentials`:
86
+
87
+ ```ini
88
+ [default]
89
+ token = eyJhbGc...
90
+ base_url = https://console.dmn.inno8cube.com
91
+
92
+ [acme-prod]
93
+ token = eyJhbGc...
94
+ base_url = https://console.dmn.acme.io
95
+ ```
96
+
97
+ Pick a profile:
98
+
99
+ ```python
100
+ client = dmn.connect(profile="acme-prod")
101
+ ```
102
+
103
+ Or via env: `export DMN_PROFILE=acme-prod`.
104
+
105
+ ## API reference
106
+
107
+ ### `dmn.connect(...)`
108
+
109
+ ```python
110
+ def connect(
111
+ token: str | None = None,
112
+ base_url: str | None = None,
113
+ *,
114
+ profile: str | None = None,
115
+ config_path: str | None = None,
116
+ timeout: float = 30,
117
+ ws_timeout: float = 30,
118
+ verify_tls: bool = True,
119
+ user_agent: str | None = None,
120
+ ) -> Client
121
+ ```
122
+
123
+ Returns a `Client`. Raises `dmn.ConfigError` if no source can supply `token` + `base_url`.
124
+
125
+ ### `Client.tenant(name)`
126
+
127
+ ```python
128
+ def tenant(self, name: str) -> Tenant
129
+ ```
130
+
131
+ `name` must be a full NDN name starting with `/`, e.g. `"/tata/dmn/acme"`. Returns a `Tenant`.
132
+
133
+ ### `Tenant.app(name)`
134
+
135
+ ```python
136
+ def app(self, name: str) -> App
137
+ ```
138
+
139
+ Returns an `App` handle.
140
+
141
+ ### Unary methods on `App`
142
+
143
+ ```python
144
+ app.get(path, **kwargs) -> Response
145
+ app.post(path, **kwargs) -> Response
146
+ app.put(path, **kwargs) -> Response
147
+ app.patch(path, **kwargs) -> Response
148
+ app.delete(path, **kwargs) -> Response
149
+ ```
150
+
151
+ `**kwargs` are passed through to `requests.Session.request` — most useful ones: `json=`, `data=`, `params=`, `headers=`, `timeout=`.
152
+
153
+ ### `App.subscribe(topic)`
154
+
155
+ ```python
156
+ def subscribe(self, topic: str) -> Iterator[Message]
157
+ ```
158
+
159
+ Opens a WebSocket to the gateway's subscribe bridge. Yields `Message(seq, topic, payload, ts)` objects as they arrive. The iterator runs until the server closes the connection or you `break`. Re-iterating opens a fresh connection.
160
+
161
+ ### `Response`
162
+
163
+ ```python
164
+ resp.status_code -> int
165
+ resp.ok -> bool # True for 2xx
166
+ resp.headers -> dict[str,str]
167
+ resp.text -> str
168
+ resp.content -> bytes
169
+ resp.json() -> Any
170
+ resp.raw -> requests.Response # power-user escape hatch
171
+ ```
172
+
173
+ ### `Message`
174
+
175
+ ```python
176
+ @dataclass(frozen=True)
177
+ class Message:
178
+ seq: int # per-topic monotonic sequence number
179
+ topic: str # which topic this message was on
180
+ payload: Any # decoded JSON body
181
+ ts: int # unix timestamp (seconds) at the publisher
182
+ ```
183
+
184
+ ### Errors
185
+
186
+ All raised by the SDK descend from `dmn.DMNError`:
187
+
188
+ | Exception | When |
189
+ |---|---|
190
+ | `dmn.ConfigError` | Missing token/base_url, malformed config file, unknown profile |
191
+ | `dmn.AuthError` | Gateway returned 401 or 403 |
192
+ | `dmn.NotFoundError` | Gateway returned 404 (tenant/app not in routing table) |
193
+ | `dmn.BackendError` | 5xx from the backend, transport failures, gateway-streamed error frame |
194
+ | `dmn.APIError` | Other non-2xx responses |
195
+
196
+ Most application code wants:
197
+
198
+ ```python
199
+ try:
200
+ resp = ingestor.post("/telemetry", json={...})
201
+ except dmn.AuthError:
202
+ # Token issue — re-mint or re-auth
203
+ raise
204
+ except dmn.NotFoundError:
205
+ # Misconfigured route
206
+ raise
207
+ except dmn.BackendError as e:
208
+ # Retryable
209
+ print(f"retrying: {e}")
210
+ ```
211
+
212
+ ## Examples
213
+
214
+ See `examples/`:
215
+
216
+ - `post_telemetry.py` — single-shot POST to the reference ingestor
217
+ - `subscribe_topic.py` — WebSocket-backed iterator consuming a topic
218
+
219
+ ## Dev notes
220
+
221
+ ```bash
222
+ # Run tests
223
+ cd sdk/python
224
+ pip install -e ".[dev]"
225
+ pytest -v
226
+
227
+ # Lint (if installed)
228
+ ruff check dmn_sdk tests
229
+ ```
230
+
231
+ ## Versioning
232
+
233
+ The SDK version tracks the DMN agent's compatible wire format. v0.282.x is the first stable line; breaking changes to the gateway's REST/WS surface will bump the major. Backwards-compatible additions bump the minor.
234
+
235
+ ## License
236
+
237
+ Restricted-Use Evaluation Licence — see `../../docs/RESTRICTED-USE-LICENSE.md`.
@@ -0,0 +1,209 @@
1
+ # dmn-sdk
2
+
3
+ Python client for the DMN mesh. Call tenant APIs from your own code with one import.
4
+
5
+ ## Install
6
+
7
+ From a checkout:
8
+
9
+ ```bash
10
+ pip install ./sdk/python/
11
+ ```
12
+
13
+ From PyPI (v0.282.3+):
14
+
15
+ ```bash
16
+ pip install dmn-sdk
17
+ ```
18
+
19
+ Requires Python 3.9+. Pulls in `requests` and `websocket-client`.
20
+
21
+ ## Quickstart
22
+
23
+ ```python
24
+ import dmn_sdk as dmn
25
+
26
+ client = dmn.connect() # reads DMN_API_TOKEN env
27
+ ingestor = client.tenant("/tata/dmn/acme").app("ingestor")
28
+
29
+ # Unary POST
30
+ resp = ingestor.post("/telemetry", json={"sensor": "s1", "value": 23.4})
31
+ print(resp.status_code, resp.json())
32
+
33
+ # Subscribe to a topic
34
+ for msg in ingestor.subscribe("telemetry"):
35
+ print(msg.seq, msg.payload)
36
+ ```
37
+
38
+ ## Authentication
39
+
40
+ Three sources, in priority order:
41
+
42
+ 1. **Explicit kwargs** to `dmn.connect(token=..., base_url=...)`
43
+ 2. **Environment**: `DMN_API_TOKEN` + `DMN_API_BASE_URL` + (optional) `DMN_PROFILE`
44
+ 3. **Config file** `~/.dmn/credentials` (INI-style)
45
+
46
+ ### Env var setup
47
+
48
+ ```bash
49
+ # Mint a token at /ui/dev/tokens/ on the dashboard,
50
+ # then export:
51
+ export DMN_API_TOKEN="eyJhbGc...the wire JWT..."
52
+ export DMN_API_BASE_URL="https://console.dmn.inno8cube.com"
53
+ ```
54
+
55
+ ### Config file setup
56
+
57
+ `~/.dmn/credentials`:
58
+
59
+ ```ini
60
+ [default]
61
+ token = eyJhbGc...
62
+ base_url = https://console.dmn.inno8cube.com
63
+
64
+ [acme-prod]
65
+ token = eyJhbGc...
66
+ base_url = https://console.dmn.acme.io
67
+ ```
68
+
69
+ Pick a profile:
70
+
71
+ ```python
72
+ client = dmn.connect(profile="acme-prod")
73
+ ```
74
+
75
+ Or via env: `export DMN_PROFILE=acme-prod`.
76
+
77
+ ## API reference
78
+
79
+ ### `dmn.connect(...)`
80
+
81
+ ```python
82
+ def connect(
83
+ token: str | None = None,
84
+ base_url: str | None = None,
85
+ *,
86
+ profile: str | None = None,
87
+ config_path: str | None = None,
88
+ timeout: float = 30,
89
+ ws_timeout: float = 30,
90
+ verify_tls: bool = True,
91
+ user_agent: str | None = None,
92
+ ) -> Client
93
+ ```
94
+
95
+ Returns a `Client`. Raises `dmn.ConfigError` if no source can supply `token` + `base_url`.
96
+
97
+ ### `Client.tenant(name)`
98
+
99
+ ```python
100
+ def tenant(self, name: str) -> Tenant
101
+ ```
102
+
103
+ `name` must be a full NDN name starting with `/`, e.g. `"/tata/dmn/acme"`. Returns a `Tenant`.
104
+
105
+ ### `Tenant.app(name)`
106
+
107
+ ```python
108
+ def app(self, name: str) -> App
109
+ ```
110
+
111
+ Returns an `App` handle.
112
+
113
+ ### Unary methods on `App`
114
+
115
+ ```python
116
+ app.get(path, **kwargs) -> Response
117
+ app.post(path, **kwargs) -> Response
118
+ app.put(path, **kwargs) -> Response
119
+ app.patch(path, **kwargs) -> Response
120
+ app.delete(path, **kwargs) -> Response
121
+ ```
122
+
123
+ `**kwargs` are passed through to `requests.Session.request` — most useful ones: `json=`, `data=`, `params=`, `headers=`, `timeout=`.
124
+
125
+ ### `App.subscribe(topic)`
126
+
127
+ ```python
128
+ def subscribe(self, topic: str) -> Iterator[Message]
129
+ ```
130
+
131
+ Opens a WebSocket to the gateway's subscribe bridge. Yields `Message(seq, topic, payload, ts)` objects as they arrive. The iterator runs until the server closes the connection or you `break`. Re-iterating opens a fresh connection.
132
+
133
+ ### `Response`
134
+
135
+ ```python
136
+ resp.status_code -> int
137
+ resp.ok -> bool # True for 2xx
138
+ resp.headers -> dict[str,str]
139
+ resp.text -> str
140
+ resp.content -> bytes
141
+ resp.json() -> Any
142
+ resp.raw -> requests.Response # power-user escape hatch
143
+ ```
144
+
145
+ ### `Message`
146
+
147
+ ```python
148
+ @dataclass(frozen=True)
149
+ class Message:
150
+ seq: int # per-topic monotonic sequence number
151
+ topic: str # which topic this message was on
152
+ payload: Any # decoded JSON body
153
+ ts: int # unix timestamp (seconds) at the publisher
154
+ ```
155
+
156
+ ### Errors
157
+
158
+ All raised by the SDK descend from `dmn.DMNError`:
159
+
160
+ | Exception | When |
161
+ |---|---|
162
+ | `dmn.ConfigError` | Missing token/base_url, malformed config file, unknown profile |
163
+ | `dmn.AuthError` | Gateway returned 401 or 403 |
164
+ | `dmn.NotFoundError` | Gateway returned 404 (tenant/app not in routing table) |
165
+ | `dmn.BackendError` | 5xx from the backend, transport failures, gateway-streamed error frame |
166
+ | `dmn.APIError` | Other non-2xx responses |
167
+
168
+ Most application code wants:
169
+
170
+ ```python
171
+ try:
172
+ resp = ingestor.post("/telemetry", json={...})
173
+ except dmn.AuthError:
174
+ # Token issue — re-mint or re-auth
175
+ raise
176
+ except dmn.NotFoundError:
177
+ # Misconfigured route
178
+ raise
179
+ except dmn.BackendError as e:
180
+ # Retryable
181
+ print(f"retrying: {e}")
182
+ ```
183
+
184
+ ## Examples
185
+
186
+ See `examples/`:
187
+
188
+ - `post_telemetry.py` — single-shot POST to the reference ingestor
189
+ - `subscribe_topic.py` — WebSocket-backed iterator consuming a topic
190
+
191
+ ## Dev notes
192
+
193
+ ```bash
194
+ # Run tests
195
+ cd sdk/python
196
+ pip install -e ".[dev]"
197
+ pytest -v
198
+
199
+ # Lint (if installed)
200
+ ruff check dmn_sdk tests
201
+ ```
202
+
203
+ ## Versioning
204
+
205
+ The SDK version tracks the DMN agent's compatible wire format. v0.282.x is the first stable line; breaking changes to the gateway's REST/WS surface will bump the major. Backwards-compatible additions bump the minor.
206
+
207
+ ## License
208
+
209
+ Restricted-Use Evaluation Licence — see `../../docs/RESTRICTED-USE-LICENSE.md`.
@@ -0,0 +1,48 @@
1
+ """dmn-sdk — Python client for the DMN mesh.
2
+
3
+ Quickstart:
4
+
5
+ import dmn
6
+
7
+ client = dmn.connect() # reads DMN_API_TOKEN env
8
+ ingestor = client.tenant("/tata/dmn/acme").app("ingestor")
9
+
10
+ resp = ingestor.post("/telemetry", json={"sensor": "s1", "value": 23.4})
11
+ print(resp.json())
12
+
13
+ for msg in ingestor.subscribe("telemetry"):
14
+ print(msg.seq, msg.payload)
15
+
16
+ See https://github.com/dmn-ucpe/dmn-ucpe/blob/main/docs/SDK.md
17
+ for the full reference.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ from ._version import __version__
23
+ from .client import Client, Response, Tenant, App, connect
24
+ from .errors import (
25
+ DMNError,
26
+ ConfigError,
27
+ AuthError,
28
+ NotFoundError,
29
+ BackendError,
30
+ APIError,
31
+ )
32
+ from .types import Message
33
+
34
+ __all__ = [
35
+ "__version__",
36
+ "connect",
37
+ "Client",
38
+ "Tenant",
39
+ "App",
40
+ "Response",
41
+ "Message",
42
+ "DMNError",
43
+ "ConfigError",
44
+ "AuthError",
45
+ "NotFoundError",
46
+ "BackendError",
47
+ "APIError",
48
+ ]
@@ -0,0 +1,9 @@
1
+ """Single source of truth for the SDK version.
2
+
3
+ Kept in its own module so it can be imported during
4
+ package build (setuptools' dynamic version lookup) +
5
+ imported at runtime by tests / log statements without
6
+ pulling in the rest of the SDK.
7
+ """
8
+
9
+ __version__ = "0.1.0"