openbox-temporal-sdk-python 1.0.0__tar.gz → 1.0.1__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. openbox_temporal_sdk_python-1.0.1/.github/workflows/publish.yml +47 -0
  2. openbox_temporal_sdk_python-1.0.1/.github/workflows/sonarqube.yaml +182 -0
  3. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/.gitignore +7 -0
  4. openbox_temporal_sdk_python-1.0.1/.repomixignore +22 -0
  5. openbox_temporal_sdk_python-1.0.1/PKG-INFO +358 -0
  6. openbox_temporal_sdk_python-1.0.1/README.md +309 -0
  7. openbox_temporal_sdk_python-1.0.1/docs/code-standards.md +742 -0
  8. openbox_temporal_sdk_python-1.0.1/docs/codebase-summary.md +503 -0
  9. openbox_temporal_sdk_python-1.0.1/docs/project-overview-pdr.md +357 -0
  10. openbox_temporal_sdk_python-1.0.1/docs/system-architecture.md +760 -0
  11. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/openbox/__init__.py +2 -0
  12. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/openbox/activities.py +1 -0
  13. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/openbox/activity_interceptor.py +8 -2
  14. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/openbox/config.py +51 -3
  15. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/openbox/worker.py +66 -71
  16. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/pyproject.toml +2 -1
  17. openbox_temporal_sdk_python-1.0.1/release-manifest.json +10908 -0
  18. openbox_temporal_sdk_python-1.0.1/repomix-output.xml +15911 -0
  19. openbox_temporal_sdk_python-1.0.1/sonar-project.properties +13 -0
  20. openbox_temporal_sdk_python-1.0.1/tests/__init__.py +2 -0
  21. openbox_temporal_sdk_python-1.0.1/tests/test_activities.py +758 -0
  22. openbox_temporal_sdk_python-1.0.1/tests/test_activity_interceptor.py +2081 -0
  23. openbox_temporal_sdk_python-1.0.1/tests/test_config.py +979 -0
  24. openbox_temporal_sdk_python-1.0.1/tests/test_otel_setup.py +1657 -0
  25. openbox_temporal_sdk_python-1.0.1/tests/test_span_processor.py +1145 -0
  26. openbox_temporal_sdk_python-1.0.1/tests/test_tracing.py +868 -0
  27. openbox_temporal_sdk_python-1.0.1/tests/test_types.py +802 -0
  28. openbox_temporal_sdk_python-1.0.1/tests/test_worker.py +1811 -0
  29. openbox_temporal_sdk_python-1.0.1/tests/test_workflow_interceptor.py +1350 -0
  30. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/uv.lock +268 -61
  31. openbox_temporal_sdk_python-1.0.0/.claude/settings.local.json +0 -8
  32. openbox_temporal_sdk_python-1.0.0/PKG-INFO +0 -1214
  33. openbox_temporal_sdk_python-1.0.0/PLAN.md +0 -234
  34. openbox_temporal_sdk_python-1.0.0/README.md +0 -1165
  35. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/.python-version +0 -0
  36. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/LICENSE +0 -0
  37. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/openbox/otel_setup.py +0 -0
  38. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/openbox/py.typed +0 -0
  39. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/openbox/span_processor.py +0 -0
  40. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/openbox/tracing.py +0 -0
  41. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/openbox/types.py +0 -0
  42. {openbox_temporal_sdk_python-1.0.0 → openbox_temporal_sdk_python-1.0.1}/openbox/workflow_interceptor.py +0 -0
@@ -0,0 +1,47 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ name: Build and Publish
11
+ runs-on: ubuntu-latest
12
+ environment: release
13
+ permissions:
14
+ id-token: write # Required for trusted publishing
15
+
16
+ steps:
17
+ - name: Checkout code
18
+ uses: actions/checkout@v4
19
+
20
+ - name: Set up Python
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.11"
24
+
25
+ - name: Install build dependencies
26
+ run: |
27
+ python -m pip install --upgrade pip
28
+ pip install hatch twine
29
+
30
+ - name: Verify version matches tag
31
+ run: |
32
+ TAG_VERSION=${GITHUB_REF#refs/tags/v}
33
+ PKG_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
34
+ if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
35
+ echo "Error: Tag version ($TAG_VERSION) does not match package version ($PKG_VERSION)"
36
+ exit 1
37
+ fi
38
+ echo "Version verified: $PKG_VERSION"
39
+
40
+ - name: Build package
41
+ run: hatch build
42
+
43
+ - name: Publish to PyPI
44
+ env:
45
+ TWINE_USERNAME: __token__
46
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
47
+ run: twine upload dist/*
@@ -0,0 +1,182 @@
1
+ # .github/workflows/sonarqube.yml
2
+ name: SonarQube
3
+
4
+ on:
5
+ push:
6
+ branches:
7
+ - main
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+ pull-requests: write
13
+ issues: write
14
+
15
+ jobs:
16
+ sonarqube:
17
+ runs-on: ubuntu-latest
18
+ env:
19
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
20
+
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ with:
24
+ fetch-depth: 0
25
+
26
+ - name: Set up Python
27
+ uses: actions/setup-python@v5
28
+ with:
29
+ python-version: '3.11'
30
+
31
+ - name: Install uv
32
+ uses: astral-sh/setup-uv@v5
33
+
34
+ - name: Install dependencies
35
+ run: uv sync --all-extras --dev
36
+
37
+ - name: Run tests with coverage
38
+ run: |
39
+ uv run pytest tests/ \
40
+ --cov=openbox \
41
+ --cov-report=xml:coverage.xml \
42
+ --cov-report=term-missing \
43
+ -v
44
+
45
+ - name: Compute Sonar project name
46
+ shell: bash
47
+ run: |
48
+ REPO_NAME="${{ github.event.repository.name }}"
49
+
50
+ if [ "${GITHUB_REF_NAME}" = "staging" ]; then
51
+ echo "SONAR_PROJECT_NAME=${REPO_NAME}-staging" >> $GITHUB_ENV
52
+ else
53
+ echo "SONAR_PROJECT_NAME=${REPO_NAME}" >> $GITHUB_ENV
54
+ fi
55
+
56
+ - name: SonarQube Scan
57
+ uses: SonarSource/sonarqube-scan-action@v5.3.0
58
+ env:
59
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
60
+ SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
61
+ with:
62
+ args: >
63
+ -Dsonar.projectKey=${{ env.SONAR_PROJECT_NAME }}
64
+ -Dsonar.projectName="${{ env.SONAR_PROJECT_NAME }}"
65
+
66
+ - name: Quality Gate
67
+ uses: SonarSource/sonarqube-quality-gate-action@v1.2.0
68
+ continue-on-error: true
69
+ env:
70
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
71
+ SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
72
+ with:
73
+ pollingTimeoutSec: 600
74
+
75
+
76
+ - name: Wait for quality gate
77
+ if: always()
78
+ run: sleep 30
79
+
80
+ - name: Build SonarQube Quality Gate report
81
+ if: always()
82
+ id: qg
83
+ env:
84
+ SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
85
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
86
+ SONAR_PROJECT_NAME: ${{ env.SONAR_PROJECT_NAME }}
87
+ run: |
88
+ set -euo pipefail
89
+
90
+ HOST_TRIM="${SONAR_HOST_URL%/}"
91
+ TOKEN="${SONAR_TOKEN}"
92
+ PKEY="${SONAR_PROJECT_NAME}" # projectKey
93
+ PNAME="${SONAR_PROJECT_NAME}" # projectName hiển thị
94
+
95
+ urlencode() { jq -nr --arg v "$1" '$v|@uri'; }
96
+
97
+ PKEY_ENC="$(urlencode "$PKEY")"
98
+ API_URL="${HOST_TRIM}/api/qualitygates/project_status?projectKey=${PKEY_ENC}"
99
+
100
+ JSON="$(curl -fsSL -u "${TOKEN}:" "$API_URL")"
101
+
102
+ STATUS="$(jq -r '.projectStatus.status' <<<"$JSON")" # OK | WARN | ERROR
103
+ QG_NAME="$(jq -r '.projectStatus.qualityGate.name // "Default"' <<<"$JSON")"
104
+ if [ -z "${SONAR_HOST_URL:-}" ]; then
105
+ echo "::warning::SONAR_HOST_URL is empty"
106
+ fi
107
+ DASH_URL="${SONAR_HOST_URL}/dashboard?id=$(urlencode "$PKEY")"
108
+
109
+ {
110
+ echo "### SonarQube Quality Gate: ${QG_NAME}"
111
+ echo
112
+ echo "- **Project**: ${PNAME}"
113
+ echo "- **Status**: **${STATUS}**"
114
+ echo "- **Detail**: [Open in SonarQube](${DASH_URL})"
115
+ } > qg.md
116
+
117
+ cat qg.md >> "$GITHUB_STEP_SUMMARY"
118
+ echo "STATUS=${STATUS}" >> "$GITHUB_ENV"
119
+ echo "QG_NAME=${QG_NAME}" >> "$GITHUB_ENV"
120
+ echo "PNAME=${PNAME}" >> "$GITHUB_ENV" # truyền sang bước sau
121
+
122
+ # (2) Upsert Issue cho
123
+ - name: Comment (or create) Issue with QG report
124
+ if: always() && env.STATUS != 'OK'
125
+ uses: actions/github-script@v7
126
+ env:
127
+ ISSUE_NUMBER: ${{ vars.SONAR_ISSUE_NUMBER }}
128
+ SONAR_PROJECT_NAME: ${{ env.SONAR_PROJECT_NAME }}
129
+ QG_NAME: ${{ env.QG_NAME }}
130
+ STATUS: ${{ env.STATUS }}
131
+ with:
132
+ script: |
133
+ const fs = require('fs');
134
+ const owner = context.repo.owner;
135
+ const repo = context.repo.repo;
136
+
137
+ const body = fs.readFileSync('qg.md', 'utf8');
138
+ const projectName = process.env.SONAR_PROJECT_NAME;
139
+ const status = process.env.STATUS;
140
+ const qgName = process.env.QG_NAME;
141
+
142
+
143
+ const stableTitle = `SonarQube QG • ${projectName}`;
144
+
145
+ const displayTitle = `${stableTitle} — ${qgName} (${status})`;
146
+
147
+ let issueNumber = process.env.ISSUE_NUMBER ? Number(process.env.ISSUE_NUMBER) : null;
148
+
149
+ if (!issueNumber) {
150
+ const { data: search } = await github.rest.search.issuesAndPullRequests({
151
+ q: `repo:${owner}/${repo} is:issue state:open in:title "${stableTitle}" label:sonarqube`
152
+ });
153
+
154
+ if (search.total_count > 0) {
155
+ issueNumber = search.items[0].number;
156
+ core.info(`Found existing issue #${issueNumber} for project "${projectName}".`);
157
+ }
158
+ }
159
+
160
+ if (issueNumber) {
161
+ try {
162
+ await github.rest.issues.update({ owner, repo, issue_number: issueNumber, title: stableTitle });
163
+ } catch (e) {
164
+ core.warning(`Failed to update title to stableTitle: ${e.message}`);
165
+ }
166
+ await github.rest.issues.createComment({ owner, repo, issue_number: issueNumber, body });
167
+ core.info(`Appended comment to issue #${issueNumber}`);
168
+ } else {
169
+ const { data: issue } = await github.rest.issues.create({
170
+ owner, repo,
171
+ title: stableTitle,
172
+ body,
173
+ labels: ['sonarqube','quality-gate']
174
+ });
175
+ core.info(`Created issue #${issue.number} for project "${projectName}"`);
176
+ }
177
+
178
+ - name: Fail job when Quality Gate not OK
179
+ if: env.STATUS != 'OK'
180
+ run: |
181
+ echo "::error title=Quality Gate Failed::STATUS=${{ env.STATUS }} (expected OK)"
182
+ exit 1
@@ -33,3 +33,10 @@ htmlcov/
33
33
  # OS
34
34
  .DS_Store
35
35
  Thumbs.db
36
+
37
+ .opencode/
38
+ AGENTS.md
39
+
40
+ .claude/
41
+ CLAUDE.md
42
+ plans/
@@ -0,0 +1,22 @@
1
+ docs/*
2
+ plans/*
3
+ assets/*
4
+ dist/*
5
+ coverage/*
6
+ build/*
7
+ ios/*
8
+ android/*
9
+ tests/*
10
+ __tests__/*
11
+ __pycache__/*
12
+ node_modules/*
13
+
14
+ .opencode/*
15
+ .claude/*
16
+ .serena/*
17
+ .pnpm-store/*
18
+ .github/*
19
+ .dart_tool/*
20
+ .idea/*
21
+ .husky/*
22
+ .venv/*
@@ -0,0 +1,358 @@
1
+ Metadata-Version: 2.4
2
+ Name: openbox-temporal-sdk-python
3
+ Version: 1.0.1
4
+ Summary: OpenBox SDK - Governance and observability for Temporal workflows
5
+ Project-URL: Homepage, https://github.com/OpenBox-AI/temporal-sdk-python
6
+ Project-URL: Documentation, https://github.com/OpenBox-AI/temporal-sdk-python#readme
7
+ Project-URL: Repository, https://github.com/OpenBox-AI/temporal-sdk-python.git
8
+ Project-URL: Issues, https://github.com/OpenBox-AI/temporal-sdk-python/issues
9
+ Project-URL: Changelog, https://github.com/OpenBox-AI/temporal-sdk-python/blob/main/CHANGELOG.md
10
+ Author-email: OpenBox Team <tino@openbox.ai>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: governance,hitl,human-in-the-loop,observability,opentelemetry,temporal,workflow
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Topic :: System :: Monitoring
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.9
26
+ Requires-Dist: asyncpg>=0.29.0
27
+ Requires-Dist: httpx<1,>=0.28.0
28
+ Requires-Dist: mysql-connector-python>=8.0.0
29
+ Requires-Dist: opentelemetry-api>=1.38.0
30
+ Requires-Dist: opentelemetry-instrumentation-asyncpg>=0.59b0
31
+ Requires-Dist: opentelemetry-instrumentation-httpx>=0.59b0
32
+ Requires-Dist: opentelemetry-instrumentation-mysql>=0.59b0
33
+ Requires-Dist: opentelemetry-instrumentation-psycopg2>=0.59b0
34
+ Requires-Dist: opentelemetry-instrumentation-pymongo>=0.59b0
35
+ Requires-Dist: opentelemetry-instrumentation-pymysql>=0.59b0
36
+ Requires-Dist: opentelemetry-instrumentation-redis>=0.59b0
37
+ Requires-Dist: opentelemetry-instrumentation-requests>=0.59b0
38
+ Requires-Dist: opentelemetry-instrumentation-sqlalchemy>=0.59b0
39
+ Requires-Dist: opentelemetry-instrumentation-urllib3>=0.59b0
40
+ Requires-Dist: opentelemetry-instrumentation-urllib>=0.59b0
41
+ Requires-Dist: opentelemetry-sdk>=1.38.0
42
+ Requires-Dist: psycopg2-binary>=2.9.10
43
+ Requires-Dist: pymongo>=4.0.0
44
+ Requires-Dist: pymysql>=1.0.0
45
+ Requires-Dist: redis>=5.0.0
46
+ Requires-Dist: sqlalchemy>=2.0.0
47
+ Requires-Dist: temporalio<2,>=1.8.0
48
+ Description-Content-Type: text/markdown
49
+
50
+ # OpenBox SDK for Temporal Workflows
51
+
52
+ OpenBox SDK provides **governance and observability** for Temporal workflows by capturing workflow/activity lifecycle events, HTTP telemetry, database queries, and file operations, then sending them to OpenBox Core for policy evaluation.
53
+
54
+ **Key Features:**
55
+ - 6 event types (WorkflowStarted, WorkflowCompleted, WorkflowFailed, SignalReceived, ActivityStarted, ActivityCompleted)
56
+ - 5-tier verdict system (ALLOW, CONSTRAIN, REQUIRE_APPROVAL, BLOCK, HALT)
57
+ - HTTP/Database/File I/O instrumentation via OpenTelemetry
58
+ - Guardrails: Input/output validation and redaction
59
+ - Human-in-the-loop approval with expiration handling
60
+ - Zero-code setup via `create_openbox_worker()` factory
61
+
62
+ ---
63
+
64
+ ## Installation
65
+
66
+ ```bash
67
+ pip install openbox-temporal-sdk-python
68
+ ```
69
+
70
+ **Requirements:**
71
+ - Python 3.9+
72
+ - Temporal SDK 1.8+
73
+ - OpenTelemetry API/SDK 1.38.0+
74
+
75
+ ---
76
+
77
+ ## Quick Start
78
+
79
+ Use the `create_openbox_worker()` factory for simple integration:
80
+
81
+ ```python
82
+ import os
83
+ from openbox import create_openbox_worker
84
+
85
+ worker = create_openbox_worker(
86
+ client=client,
87
+ task_queue="my-task-queue",
88
+ workflows=[MyWorkflow],
89
+ activities=[my_activity],
90
+ # OpenBox config
91
+ openbox_url=os.getenv("OPENBOX_URL"),
92
+ openbox_api_key=os.getenv("OPENBOX_API_KEY"),
93
+ )
94
+
95
+ await worker.run()
96
+ ```
97
+
98
+ The factory automatically:
99
+ 1. Validates the API key
100
+ 2. Creates span processor
101
+ 3. Sets up OpenTelemetry instrumentation
102
+ 4. Creates governance interceptors
103
+ 5. Adds `send_governance_event` activity
104
+ 6. Returns fully configured Worker
105
+
106
+ ---
107
+
108
+ ## Configuration
109
+
110
+ ### Environment Variables
111
+
112
+ ```bash
113
+ OPENBOX_URL=http://localhost:8086
114
+ OPENBOX_API_KEY=obx_test_key_1
115
+ OPENBOX_GOVERNANCE_TIMEOUT=30.0
116
+ OPENBOX_GOVERNANCE_POLICY=fail_open # or fail_closed
117
+ ```
118
+
119
+ ### Factory Function Parameters
120
+
121
+ ```python
122
+ worker = create_openbox_worker(
123
+ client=client,
124
+ task_queue="my-task-queue",
125
+ workflows=[MyWorkflow],
126
+ activities=[my_activity],
127
+
128
+ # OpenBox config
129
+ openbox_url="http://localhost:8086",
130
+ openbox_api_key="obx_test_key_1",
131
+ governance_timeout=30.0,
132
+ governance_policy="fail_open",
133
+
134
+ # Event filtering
135
+ send_start_event=True,
136
+ send_activity_start_event=True,
137
+ skip_workflow_types={"InternalWorkflow"},
138
+ skip_activity_types={"send_governance_event"},
139
+ skip_signals={"heartbeat"},
140
+
141
+ # Database instrumentation
142
+ instrument_databases=True,
143
+ db_libraries={"psycopg2", "redis"}, # None = all available
144
+
145
+ # File I/O instrumentation
146
+ instrument_file_io=False, # disabled by default
147
+
148
+ # Standard Worker options (all supported)
149
+ activity_executor=my_executor,
150
+ max_concurrent_activities=10,
151
+ )
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Governance Verdicts
157
+
158
+ OpenBox Core returns a verdict indicating what action the SDK should take.
159
+
160
+ | Verdict | Behavior |
161
+ |---------|----------|
162
+ | `ALLOW` | Continue execution normally |
163
+ | `CONSTRAIN` | Log constraints, continue |
164
+ | `REQUIRE_APPROVAL` | Pause, poll for human approval |
165
+ | `BLOCK` | Raise error, stop activity |
166
+ | `HALT` | Raise error, terminate workflow |
167
+
168
+ **v1.0 Backward Compatibility:**
169
+ - `"continue"` → `ALLOW`
170
+ - `"stop"` → `HALT`
171
+ - `"require-approval"` → `REQUIRE_APPROVAL`
172
+
173
+ ---
174
+
175
+ ## Event Types
176
+
177
+ | Event | Trigger | Captured Fields |
178
+ |-------|---------|-----------------|
179
+ | WorkflowStarted | Workflow begins | workflow_id, run_id, workflow_type, task_queue |
180
+ | WorkflowCompleted | Workflow succeeds | workflow_id, run_id, workflow_type |
181
+ | WorkflowFailed | Workflow fails | workflow_id, run_id, workflow_type, error |
182
+ | SignalReceived | Signal received | workflow_id, signal_name, signal_args |
183
+ | ActivityStarted | Activity begins | activity_id, activity_type, activity_input |
184
+ | ActivityCompleted | Activity ends | activity_id, activity_type, activity_input, activity_output, spans, status, duration |
185
+
186
+ ---
187
+
188
+ ## Guardrails (Input/Output Redaction)
189
+
190
+ OpenBox Core can validate and redact sensitive data before/after activity execution:
191
+
192
+ ```python
193
+ # Request
194
+ {
195
+ "verdict": "allow",
196
+ "guardrails_result": {
197
+ "input_type": "activity_input",
198
+ "redacted_input": {"prompt": "[REDACTED]", "user_id": "123"},
199
+ "validation_passed": true,
200
+ "reasons": []
201
+ }
202
+ }
203
+
204
+ # If validation fails:
205
+ {
206
+ "validation_passed": false,
207
+ "reasons": [
208
+ {"type": "pii", "field": "email", "reason": "Contains PII"}
209
+ ]
210
+ }
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Error Handling
216
+
217
+ Configure error policy via `on_api_error`:
218
+
219
+ | Policy | Behavior |
220
+ |--------|----------|
221
+ | `fail_open` (default) | If governance API fails, allow workflow to continue |
222
+ | `fail_closed` | If governance API fails, terminate workflow |
223
+
224
+ ---
225
+
226
+ ## Supported Instrumentation
227
+
228
+ ### HTTP Libraries
229
+ - `httpx` (sync + async) - full body capture
230
+ - `requests` - full body capture
231
+ - `urllib3` - full body capture
232
+ - `urllib` - request body only
233
+
234
+ ### Databases
235
+ - PostgreSQL: `psycopg2`, `asyncpg`
236
+ - MySQL: `mysql-connector-python`, `pymysql`
237
+ - MongoDB: `pymongo`
238
+ - Redis: `redis`
239
+ - ORM: `sqlalchemy`
240
+
241
+ ### File I/O
242
+ - `open()`, `read()`, `write()`, `readline()`, `readlines()`
243
+ - Skips system paths (`/dev/`, `/proc/`, `/sys/`, `__pycache__`)
244
+
245
+ ---
246
+
247
+ ## Architecture
248
+
249
+ See [System Architecture](./docs/system-architecture.md) for detailed component design.
250
+
251
+ **High-Level Flow:**
252
+
253
+ ```
254
+ Workflow/Activity → Interceptors → Span Processor → OpenBox Core API
255
+
256
+ Returns Verdict
257
+
258
+ (ALLOW, BLOCK, HALT, etc.)
259
+ ```
260
+
261
+ ---
262
+
263
+ ## Advanced Usage
264
+
265
+ For manual control, import individual components:
266
+
267
+ ```python
268
+ from openbox import (
269
+ initialize,
270
+ WorkflowSpanProcessor,
271
+ GovernanceInterceptor,
272
+ GovernanceConfig,
273
+ )
274
+ from openbox.otel_setup import setup_opentelemetry_for_governance
275
+ from openbox.activity_interceptor import ActivityGovernanceInterceptor
276
+ from openbox.activities import send_governance_event
277
+
278
+ # 1. Initialize SDK
279
+ initialize(api_url="http://localhost:8086", api_key="obx_test_key_1")
280
+
281
+ # 2. Create span processor
282
+ span_processor = WorkflowSpanProcessor(
283
+ ignored_url_prefixes=["http://localhost:8086"]
284
+ )
285
+
286
+ # 3. Setup OTel instrumentation
287
+ setup_opentelemetry_for_governance(span_processor)
288
+
289
+ # 4. Create governance config
290
+ config = GovernanceConfig(
291
+ on_api_error="fail_closed",
292
+ api_timeout=30.0,
293
+ )
294
+
295
+ # 5. Create interceptors
296
+ workflow_interceptor = GovernanceInterceptor(
297
+ api_url="http://localhost:8086",
298
+ api_key="obx_test_key_1",
299
+ span_processor=span_processor,
300
+ config=config,
301
+ )
302
+
303
+ activity_interceptor = ActivityGovernanceInterceptor(
304
+ api_url="http://localhost:8086",
305
+ api_key="obx_test_key_1",
306
+ span_processor=span_processor,
307
+ config=config,
308
+ )
309
+
310
+ # 6. Create worker
311
+ from temporalio.worker import Worker
312
+ worker = Worker(
313
+ client=client,
314
+ task_queue="my-task-queue",
315
+ workflows=[MyWorkflow],
316
+ activities=[my_activity, send_governance_event],
317
+ interceptors=[workflow_interceptor, activity_interceptor],
318
+ )
319
+ ```
320
+
321
+ ---
322
+
323
+ ## Documentation
324
+
325
+ - **[Project Overview & PDR](./docs/project-overview-pdr.md)** - Requirements, features, constraints
326
+ - **[System Architecture](./docs/system-architecture.md)** - Component design, data flows, security
327
+ - **[Codebase Summary](./docs/codebase-summary.md)** - Code structure and component details
328
+ - **[Code Standards](./docs/code-standards.md)** - Coding conventions and best practices
329
+ - **[Project Roadmap](./docs/project-roadmap.md)** - Future enhancements and timeline
330
+
331
+ ---
332
+
333
+ ## Testing
334
+
335
+ The SDK includes comprehensive test coverage with 10 test files:
336
+
337
+ ```bash
338
+ pytest tests/
339
+ ```
340
+
341
+ Test files: `test_activities.py`, `test_activity_interceptor.py`, `test_config.py`, `test_otel_setup.py`, `test_span_processor.py`, `test_tracing.py`, `test_types.py`, `test_worker.py`, `test_workflow_interceptor.py`
342
+
343
+ ---
344
+
345
+ ## License
346
+
347
+ MIT License - See LICENSE file for details
348
+
349
+ ---
350
+
351
+ ## Support
352
+
353
+ - **Issues:** GitHub Issues
354
+ - **Documentation:** See `./docs/`
355
+
356
+ ---
357
+
358
+ **Version:** 1.0.0 | **Last Updated:** 2026-02-04