optropic 1.0.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.
- optropic-1.0.0/.gitignore +58 -0
- optropic-1.0.0/LICENSE +21 -0
- optropic-1.0.0/PKG-INFO +221 -0
- optropic-1.0.0/README.md +191 -0
- optropic-1.0.0/optropic/__init__.py +17 -0
- optropic-1.0.0/optropic/client.py +95 -0
- optropic-1.0.0/optropic/errors.py +24 -0
- optropic-1.0.0/optropic/py.typed +0 -0
- optropic-1.0.0/optropic/types.py +73 -0
- optropic-1.0.0/pyproject.toml +44 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
2
|
+
|
|
3
|
+
# dependencies
|
|
4
|
+
node_modules/
|
|
5
|
+
/.pnp
|
|
6
|
+
.pnp.*
|
|
7
|
+
.yarn/*
|
|
8
|
+
!.yarn/patches
|
|
9
|
+
!.yarn/plugins
|
|
10
|
+
!.yarn/releases
|
|
11
|
+
!.yarn/versions
|
|
12
|
+
|
|
13
|
+
# testing
|
|
14
|
+
coverage/
|
|
15
|
+
.coverage
|
|
16
|
+
htmlcov/
|
|
17
|
+
|
|
18
|
+
# next.js
|
|
19
|
+
.next/
|
|
20
|
+
out/
|
|
21
|
+
|
|
22
|
+
# production
|
|
23
|
+
build/
|
|
24
|
+
dist/
|
|
25
|
+
|
|
26
|
+
# turbo
|
|
27
|
+
.turbo/
|
|
28
|
+
|
|
29
|
+
# misc
|
|
30
|
+
.DS_Store
|
|
31
|
+
*.pem
|
|
32
|
+
|
|
33
|
+
# debug
|
|
34
|
+
npm-debug.log*
|
|
35
|
+
yarn-debug.log*
|
|
36
|
+
yarn-error.log*
|
|
37
|
+
.pnpm-debug.log*
|
|
38
|
+
|
|
39
|
+
# env files (can opt-in for committing if needed)
|
|
40
|
+
.env*
|
|
41
|
+
|
|
42
|
+
# vercel
|
|
43
|
+
.vercel
|
|
44
|
+
|
|
45
|
+
# typescript
|
|
46
|
+
*.tsbuildinfo
|
|
47
|
+
next-env.d.ts
|
|
48
|
+
crates/
|
|
49
|
+
|
|
50
|
+
# Build outputs
|
|
51
|
+
.next/
|
|
52
|
+
dist/
|
|
53
|
+
lib/
|
|
54
|
+
|
|
55
|
+
# Test outputs
|
|
56
|
+
playwright-report/
|
|
57
|
+
test-results/
|
|
58
|
+
.env*.local
|
optropic-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Virtrex GmbH
|
|
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.
|
optropic-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: optropic
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Official Python SDK for Optropic product authentication and verification
|
|
5
|
+
Project-URL: Homepage, https://optropic.com
|
|
6
|
+
Project-URL: Documentation, https://docs.optropic.com/sdk/python
|
|
7
|
+
Project-URL: Repository, https://github.com/optropic/sdk-python
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/optropic/sdk-python/issues
|
|
9
|
+
Author-email: Virtrex GmbH <dev@optropic.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: anti-counterfeit,authentication,optropic,product-verification,verification
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Security
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Requires-Dist: httpx<1.0.0,>=0.24.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# optropic
|
|
32
|
+
|
|
33
|
+
Official Python SDK for Optropic product authentication.
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/optropic/)
|
|
36
|
+
[](https://pypi.org/project/optropic/)
|
|
37
|
+
[](https://opensource.org/licenses/MIT)
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- **Simple Authentication** - Single API key for all operations
|
|
42
|
+
- **Type-Safe** - Full type annotations with `py.typed` support
|
|
43
|
+
- **Fast Integration** - Copy-paste quickstart, production-ready
|
|
44
|
+
- **Vertical Agnostic** - Same API for pharma, luxury, art, and more
|
|
45
|
+
- **Error Transparency** - Clear error codes, actionable messages
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install optropic
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from optropic import OptropicClient
|
|
57
|
+
|
|
58
|
+
# Initialize the client
|
|
59
|
+
client = OptropicClient(api_key="op_live_xxxxxxxxxxxx")
|
|
60
|
+
|
|
61
|
+
# Verify an asset
|
|
62
|
+
result = client.verify("asset-id-or-short-id")
|
|
63
|
+
|
|
64
|
+
if result.signature_valid:
|
|
65
|
+
print("Product is genuine!")
|
|
66
|
+
print(f"Security level: {result.security_level}")
|
|
67
|
+
else:
|
|
68
|
+
print("Warning: verification failed")
|
|
69
|
+
|
|
70
|
+
# Or use as a context manager
|
|
71
|
+
with OptropicClient(api_key="op_live_xxxxxxxxxxxx") as client:
|
|
72
|
+
result = client.verify("asset-id-or-short-id")
|
|
73
|
+
print(result.status)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## API Reference
|
|
77
|
+
|
|
78
|
+
### OptropicClient
|
|
79
|
+
|
|
80
|
+
The main client class for all SDK operations.
|
|
81
|
+
|
|
82
|
+
#### Constructor
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
client = OptropicClient(
|
|
86
|
+
api_key="op_live_xxx", # Required: Your API key
|
|
87
|
+
base_url="https://...", # Optional: For self-hosted deployments
|
|
88
|
+
timeout=30.0, # Optional: Request timeout in seconds (default: 30)
|
|
89
|
+
)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Methods
|
|
93
|
+
|
|
94
|
+
#### verify(asset_id: str) -> VerifyResult
|
|
95
|
+
|
|
96
|
+
Verify an asset by ID or short_id.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
result = client.verify("asset-id-or-short-id")
|
|
100
|
+
|
|
101
|
+
# Result fields
|
|
102
|
+
result.id # str: Asset ID
|
|
103
|
+
result.status # str: Verification status
|
|
104
|
+
result.security_level # str: 'signed', 'sealed', etc.
|
|
105
|
+
result.signature_valid # bool: Whether signature is valid
|
|
106
|
+
result.verification_count # int: Number of times verified
|
|
107
|
+
result.last_verified_at # str: ISO timestamp of last verification
|
|
108
|
+
result.revocation_status # str: 'active' or 'revoked'
|
|
109
|
+
result.verification_mode # str: 'online' or 'offline'
|
|
110
|
+
result.provenance_valid # Optional[bool]: Provenance chain validity
|
|
111
|
+
result.evidence # Optional[VerificationEvidence]: Detailed evidence
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Error Handling
|
|
115
|
+
|
|
116
|
+
All errors extend `OptropicError` with clear error codes:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from optropic import (
|
|
120
|
+
OptropicClient,
|
|
121
|
+
OptropicError,
|
|
122
|
+
AuthenticationError,
|
|
123
|
+
NotFoundError,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
result = client.verify("asset-id")
|
|
128
|
+
except NotFoundError:
|
|
129
|
+
print("Asset not found in system.")
|
|
130
|
+
except AuthenticationError:
|
|
131
|
+
print("Invalid API key.")
|
|
132
|
+
except OptropicError as e:
|
|
133
|
+
print(f"Error {e.code}: {e}")
|
|
134
|
+
print(f"HTTP status: {e.status}")
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Error Types
|
|
138
|
+
|
|
139
|
+
| Error | Code | Description |
|
|
140
|
+
|-------|------|-------------|
|
|
141
|
+
| `AuthenticationError` | `INVALID_API_KEY` | API key is invalid or missing |
|
|
142
|
+
| `NotFoundError` | `NOT_FOUND` | Asset doesn't exist in the system |
|
|
143
|
+
| `OptropicError` | varies | Base error for all other API errors |
|
|
144
|
+
|
|
145
|
+
## Types
|
|
146
|
+
|
|
147
|
+
All types are frozen dataclasses with full type annotations:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from optropic import VerifyResult, VerificationEvidence
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
- `VerifyResult` - Result of verifying an asset
|
|
154
|
+
- `VerificationEvidence` - Structured evidence from verification
|
|
155
|
+
|
|
156
|
+
## Environment Variables
|
|
157
|
+
|
|
158
|
+
Recommended setup for production:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# .env
|
|
162
|
+
OPTROPIC_API_KEY=op_live_xxxxxxxxxxxx
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
import os
|
|
167
|
+
from optropic import OptropicClient
|
|
168
|
+
|
|
169
|
+
client = OptropicClient(api_key=os.environ["OPTROPIC_API_KEY"])
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Examples
|
|
173
|
+
|
|
174
|
+
### Flask Backend
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from flask import Flask, request, jsonify
|
|
178
|
+
from optropic import OptropicClient, NotFoundError
|
|
179
|
+
|
|
180
|
+
app = Flask(__name__)
|
|
181
|
+
client = OptropicClient(api_key=os.environ["OPTROPIC_API_KEY"])
|
|
182
|
+
|
|
183
|
+
@app.route("/api/verify", methods=["POST"])
|
|
184
|
+
def verify():
|
|
185
|
+
try:
|
|
186
|
+
result = client.verify(request.json["asset_id"])
|
|
187
|
+
return jsonify({
|
|
188
|
+
"status": result.status,
|
|
189
|
+
"signature_valid": result.signature_valid,
|
|
190
|
+
"security_level": result.security_level,
|
|
191
|
+
})
|
|
192
|
+
except NotFoundError:
|
|
193
|
+
return jsonify({"error": "Asset not found"}), 404
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Django View
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
import os
|
|
200
|
+
from django.http import JsonResponse
|
|
201
|
+
from optropic import OptropicClient
|
|
202
|
+
|
|
203
|
+
client = OptropicClient(api_key=os.environ["OPTROPIC_API_KEY"])
|
|
204
|
+
|
|
205
|
+
def verify_asset(request, asset_id):
|
|
206
|
+
result = client.verify(asset_id)
|
|
207
|
+
return JsonResponse({
|
|
208
|
+
"status": result.status,
|
|
209
|
+
"signature_valid": result.signature_valid,
|
|
210
|
+
})
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Support
|
|
214
|
+
|
|
215
|
+
- [Documentation](https://docs.optropic.com)
|
|
216
|
+
- [Email Support](mailto:support@optropic.com)
|
|
217
|
+
- [Issue Tracker](https://github.com/optropic/sdk-python/issues)
|
|
218
|
+
|
|
219
|
+
## License
|
|
220
|
+
|
|
221
|
+
MIT - [Virtrex GmbH](https://optropic.com)
|
optropic-1.0.0/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# optropic
|
|
2
|
+
|
|
3
|
+
Official Python SDK for Optropic product authentication.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/optropic/)
|
|
6
|
+
[](https://pypi.org/project/optropic/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Simple Authentication** - Single API key for all operations
|
|
12
|
+
- **Type-Safe** - Full type annotations with `py.typed` support
|
|
13
|
+
- **Fast Integration** - Copy-paste quickstart, production-ready
|
|
14
|
+
- **Vertical Agnostic** - Same API for pharma, luxury, art, and more
|
|
15
|
+
- **Error Transparency** - Clear error codes, actionable messages
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install optropic
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from optropic import OptropicClient
|
|
27
|
+
|
|
28
|
+
# Initialize the client
|
|
29
|
+
client = OptropicClient(api_key="op_live_xxxxxxxxxxxx")
|
|
30
|
+
|
|
31
|
+
# Verify an asset
|
|
32
|
+
result = client.verify("asset-id-or-short-id")
|
|
33
|
+
|
|
34
|
+
if result.signature_valid:
|
|
35
|
+
print("Product is genuine!")
|
|
36
|
+
print(f"Security level: {result.security_level}")
|
|
37
|
+
else:
|
|
38
|
+
print("Warning: verification failed")
|
|
39
|
+
|
|
40
|
+
# Or use as a context manager
|
|
41
|
+
with OptropicClient(api_key="op_live_xxxxxxxxxxxx") as client:
|
|
42
|
+
result = client.verify("asset-id-or-short-id")
|
|
43
|
+
print(result.status)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## API Reference
|
|
47
|
+
|
|
48
|
+
### OptropicClient
|
|
49
|
+
|
|
50
|
+
The main client class for all SDK operations.
|
|
51
|
+
|
|
52
|
+
#### Constructor
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
client = OptropicClient(
|
|
56
|
+
api_key="op_live_xxx", # Required: Your API key
|
|
57
|
+
base_url="https://...", # Optional: For self-hosted deployments
|
|
58
|
+
timeout=30.0, # Optional: Request timeout in seconds (default: 30)
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Methods
|
|
63
|
+
|
|
64
|
+
#### verify(asset_id: str) -> VerifyResult
|
|
65
|
+
|
|
66
|
+
Verify an asset by ID or short_id.
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
result = client.verify("asset-id-or-short-id")
|
|
70
|
+
|
|
71
|
+
# Result fields
|
|
72
|
+
result.id # str: Asset ID
|
|
73
|
+
result.status # str: Verification status
|
|
74
|
+
result.security_level # str: 'signed', 'sealed', etc.
|
|
75
|
+
result.signature_valid # bool: Whether signature is valid
|
|
76
|
+
result.verification_count # int: Number of times verified
|
|
77
|
+
result.last_verified_at # str: ISO timestamp of last verification
|
|
78
|
+
result.revocation_status # str: 'active' or 'revoked'
|
|
79
|
+
result.verification_mode # str: 'online' or 'offline'
|
|
80
|
+
result.provenance_valid # Optional[bool]: Provenance chain validity
|
|
81
|
+
result.evidence # Optional[VerificationEvidence]: Detailed evidence
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Error Handling
|
|
85
|
+
|
|
86
|
+
All errors extend `OptropicError` with clear error codes:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from optropic import (
|
|
90
|
+
OptropicClient,
|
|
91
|
+
OptropicError,
|
|
92
|
+
AuthenticationError,
|
|
93
|
+
NotFoundError,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
result = client.verify("asset-id")
|
|
98
|
+
except NotFoundError:
|
|
99
|
+
print("Asset not found in system.")
|
|
100
|
+
except AuthenticationError:
|
|
101
|
+
print("Invalid API key.")
|
|
102
|
+
except OptropicError as e:
|
|
103
|
+
print(f"Error {e.code}: {e}")
|
|
104
|
+
print(f"HTTP status: {e.status}")
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Error Types
|
|
108
|
+
|
|
109
|
+
| Error | Code | Description |
|
|
110
|
+
|-------|------|-------------|
|
|
111
|
+
| `AuthenticationError` | `INVALID_API_KEY` | API key is invalid or missing |
|
|
112
|
+
| `NotFoundError` | `NOT_FOUND` | Asset doesn't exist in the system |
|
|
113
|
+
| `OptropicError` | varies | Base error for all other API errors |
|
|
114
|
+
|
|
115
|
+
## Types
|
|
116
|
+
|
|
117
|
+
All types are frozen dataclasses with full type annotations:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from optropic import VerifyResult, VerificationEvidence
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
- `VerifyResult` - Result of verifying an asset
|
|
124
|
+
- `VerificationEvidence` - Structured evidence from verification
|
|
125
|
+
|
|
126
|
+
## Environment Variables
|
|
127
|
+
|
|
128
|
+
Recommended setup for production:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# .env
|
|
132
|
+
OPTROPIC_API_KEY=op_live_xxxxxxxxxxxx
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
import os
|
|
137
|
+
from optropic import OptropicClient
|
|
138
|
+
|
|
139
|
+
client = OptropicClient(api_key=os.environ["OPTROPIC_API_KEY"])
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Examples
|
|
143
|
+
|
|
144
|
+
### Flask Backend
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from flask import Flask, request, jsonify
|
|
148
|
+
from optropic import OptropicClient, NotFoundError
|
|
149
|
+
|
|
150
|
+
app = Flask(__name__)
|
|
151
|
+
client = OptropicClient(api_key=os.environ["OPTROPIC_API_KEY"])
|
|
152
|
+
|
|
153
|
+
@app.route("/api/verify", methods=["POST"])
|
|
154
|
+
def verify():
|
|
155
|
+
try:
|
|
156
|
+
result = client.verify(request.json["asset_id"])
|
|
157
|
+
return jsonify({
|
|
158
|
+
"status": result.status,
|
|
159
|
+
"signature_valid": result.signature_valid,
|
|
160
|
+
"security_level": result.security_level,
|
|
161
|
+
})
|
|
162
|
+
except NotFoundError:
|
|
163
|
+
return jsonify({"error": "Asset not found"}), 404
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Django View
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
import os
|
|
170
|
+
from django.http import JsonResponse
|
|
171
|
+
from optropic import OptropicClient
|
|
172
|
+
|
|
173
|
+
client = OptropicClient(api_key=os.environ["OPTROPIC_API_KEY"])
|
|
174
|
+
|
|
175
|
+
def verify_asset(request, asset_id):
|
|
176
|
+
result = client.verify(asset_id)
|
|
177
|
+
return JsonResponse({
|
|
178
|
+
"status": result.status,
|
|
179
|
+
"signature_valid": result.signature_valid,
|
|
180
|
+
})
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Support
|
|
184
|
+
|
|
185
|
+
- [Documentation](https://docs.optropic.com)
|
|
186
|
+
- [Email Support](mailto:support@optropic.com)
|
|
187
|
+
- [Issue Tracker](https://github.com/optropic/sdk-python/issues)
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT - [Virtrex GmbH](https://optropic.com)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Optropic Python SDK — asset verification and compliance."""
|
|
2
|
+
|
|
3
|
+
from .client import OptropicClient
|
|
4
|
+
from .types import VerifyResult, VerificationEvidence
|
|
5
|
+
from .errors import OptropicError, AuthenticationError, NotFoundError
|
|
6
|
+
|
|
7
|
+
__version__ = "1.0.0"
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"OptropicClient",
|
|
11
|
+
"VerifyResult",
|
|
12
|
+
"VerificationEvidence",
|
|
13
|
+
"OptropicError",
|
|
14
|
+
"AuthenticationError",
|
|
15
|
+
"NotFoundError",
|
|
16
|
+
"__version__",
|
|
17
|
+
]
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Optropic Python SDK client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from .types import VerifyResult, VerificationEvidence
|
|
8
|
+
from .errors import OptropicError, AuthenticationError, NotFoundError
|
|
9
|
+
|
|
10
|
+
DEFAULT_BASE_URL = "https://api.optropic.com"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class OptropicClient:
|
|
14
|
+
"""Client for the Optropic API.
|
|
15
|
+
|
|
16
|
+
Usage::
|
|
17
|
+
|
|
18
|
+
client = OptropicClient(api_key="op_live_xxx")
|
|
19
|
+
result = client.verify("asset-id-or-short-id")
|
|
20
|
+
print(result.signature_valid)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
api_key: str,
|
|
26
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
27
|
+
timeout: float = 30.0,
|
|
28
|
+
):
|
|
29
|
+
self._api_key = api_key
|
|
30
|
+
self._base_url = base_url.rstrip("/")
|
|
31
|
+
self._client = httpx.Client(
|
|
32
|
+
base_url=self._base_url,
|
|
33
|
+
headers={"x-api-key": api_key},
|
|
34
|
+
timeout=timeout,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def verify(self, asset_id: str) -> VerifyResult:
|
|
38
|
+
"""Verify an asset by ID or short_id.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
asset_id: UUID or short_id of the asset.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
VerifyResult with signature validity and revocation status.
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
NotFoundError: If the asset does not exist.
|
|
48
|
+
AuthenticationError: If the API key is invalid.
|
|
49
|
+
OptropicError: For other API errors.
|
|
50
|
+
"""
|
|
51
|
+
response = self._client.get(f"/v1/assets/{asset_id}/verify")
|
|
52
|
+
data = response.json()
|
|
53
|
+
|
|
54
|
+
if response.status_code == 404:
|
|
55
|
+
raise NotFoundError(data.get("message", "Asset not found"))
|
|
56
|
+
if response.status_code == 401:
|
|
57
|
+
raise AuthenticationError(data.get("message", "Unauthorized"))
|
|
58
|
+
if response.status_code >= 400:
|
|
59
|
+
raise OptropicError(
|
|
60
|
+
message=data.get("message", "Request failed"),
|
|
61
|
+
code=data.get("code", "UNKNOWN_ERROR"),
|
|
62
|
+
status=response.status_code,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
evidence = VerificationEvidence(
|
|
66
|
+
signature_valid=data.get("signatureValid", False),
|
|
67
|
+
revocation_status=data.get("revocationStatus", "active"),
|
|
68
|
+
verification_mode=data.get("verificationMode", "online"),
|
|
69
|
+
provenance_valid=data.get("provenanceValid"),
|
|
70
|
+
security_level=data.get("securityLevel"),
|
|
71
|
+
verification_count=data.get("verificationCount"),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return VerifyResult(
|
|
75
|
+
id=data["id"],
|
|
76
|
+
status=data["status"],
|
|
77
|
+
security_level=data.get("securityLevel", "signed"),
|
|
78
|
+
signature_valid=data.get("signatureValid", False),
|
|
79
|
+
verification_count=data.get("verificationCount", 0),
|
|
80
|
+
last_verified_at=data.get("lastVerifiedAt", ""),
|
|
81
|
+
revocation_status=data.get("revocationStatus", "active"),
|
|
82
|
+
verification_mode=data.get("verificationMode", "online"),
|
|
83
|
+
provenance_valid=data.get("provenanceValid"),
|
|
84
|
+
evidence=evidence,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def close(self) -> None:
|
|
88
|
+
"""Close the HTTP client."""
|
|
89
|
+
self._client.close()
|
|
90
|
+
|
|
91
|
+
def __enter__(self) -> "OptropicClient":
|
|
92
|
+
return self
|
|
93
|
+
|
|
94
|
+
def __exit__(self, *args: object) -> None:
|
|
95
|
+
self.close()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Error types for the Optropic Python SDK."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class OptropicError(Exception):
|
|
5
|
+
"""Base error for all Optropic SDK errors."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, message: str, code: str = "UNKNOWN_ERROR", status: int = 0):
|
|
8
|
+
super().__init__(message)
|
|
9
|
+
self.code = code
|
|
10
|
+
self.status = status
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AuthenticationError(OptropicError):
|
|
14
|
+
"""Raised when API key is invalid or missing."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, message: str = "Invalid or missing API key"):
|
|
17
|
+
super().__init__(message, code="INVALID_API_KEY", status=401)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NotFoundError(OptropicError):
|
|
21
|
+
"""Raised when a resource is not found."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, message: str = "Resource not found"):
|
|
24
|
+
super().__init__(message, code="NOT_FOUND", status=404)
|
|
File without changes
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Type definitions for the Optropic Python SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class VerificationEvidence:
|
|
11
|
+
"""Structured evidence from an online verification."""
|
|
12
|
+
|
|
13
|
+
signature_valid: bool
|
|
14
|
+
revocation_status: str # 'active' or 'revoked'
|
|
15
|
+
verification_mode: str # 'online' or 'offline'
|
|
16
|
+
provenance_valid: Optional[bool] = None
|
|
17
|
+
security_level: Optional[str] = None
|
|
18
|
+
verification_count: Optional[int] = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class VerifyResult:
|
|
23
|
+
"""Result of verifying an asset."""
|
|
24
|
+
|
|
25
|
+
id: str
|
|
26
|
+
status: str
|
|
27
|
+
security_level: str
|
|
28
|
+
signature_valid: bool
|
|
29
|
+
verification_count: int
|
|
30
|
+
last_verified_at: str
|
|
31
|
+
revocation_status: str
|
|
32
|
+
verification_mode: str
|
|
33
|
+
provenance_valid: Optional[bool] = None
|
|
34
|
+
evidence: Optional[VerificationEvidence] = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True)
|
|
38
|
+
class RekorAnchor:
|
|
39
|
+
"""Rekor transparency log anchor metadata."""
|
|
40
|
+
|
|
41
|
+
log_index: int
|
|
42
|
+
integrated_time: Optional[str] = None
|
|
43
|
+
entry_url: Optional[str] = None
|
|
44
|
+
verification_url: Optional[str] = None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class MerkleRootRecord:
|
|
49
|
+
"""Daily Merkle root with transparency log anchor status."""
|
|
50
|
+
|
|
51
|
+
id: str
|
|
52
|
+
root_hash: str
|
|
53
|
+
event_count: int
|
|
54
|
+
anchor_status: str # 'pending', 'anchored', 'failed', 'skipped'
|
|
55
|
+
date: str
|
|
56
|
+
created_at: Optional[str] = None
|
|
57
|
+
rekor: Optional[RekorAnchor] = None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(frozen=True)
|
|
61
|
+
class ComplianceAttestation:
|
|
62
|
+
"""Daily compliance attestation record."""
|
|
63
|
+
|
|
64
|
+
id: str
|
|
65
|
+
attestation_date: str
|
|
66
|
+
total_events: int
|
|
67
|
+
chain_integrity_valid: bool
|
|
68
|
+
rekor_anchored: bool
|
|
69
|
+
signature_hex: str
|
|
70
|
+
attestation_hash: str
|
|
71
|
+
previous_attestation_hash: Optional[str] = None
|
|
72
|
+
tenant_id: Optional[str] = None
|
|
73
|
+
created_at: Optional[str] = None
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "optropic"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Official Python SDK for Optropic product authentication and verification"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [{name = "Virtrex GmbH", email = "dev@optropic.com"}]
|
|
13
|
+
keywords = ["optropic", "authentication", "anti-counterfeit", "product-verification", "verification"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
"Topic :: Security",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"httpx>=0.24.0,<1.0.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://optropic.com"
|
|
33
|
+
Documentation = "https://docs.optropic.com/sdk/python"
|
|
34
|
+
Repository = "https://github.com/optropic/sdk-python"
|
|
35
|
+
"Bug Tracker" = "https://github.com/optropic/sdk-python/issues"
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
dev = [
|
|
39
|
+
"pytest>=7.0",
|
|
40
|
+
"pytest-asyncio>=0.21",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.wheel]
|
|
44
|
+
packages = ["optropic"]
|