teaql 0.2.6__tar.gz → 0.2.7__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.
- {teaql-0.2.6 → teaql-0.2.7}/PKG-INFO +44 -1
- {teaql-0.2.6 → teaql-0.2.7}/README.md +43 -0
- {teaql-0.2.6 → teaql-0.2.7}/pyproject.toml +1 -1
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/runtime/audit.py +13 -4
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/runtime/context.py +8 -2
- teaql-0.2.7/src/teaql/runtime/log_privacy.py +93 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql.egg-info/PKG-INFO +44 -1
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql.egg-info/SOURCES.txt +1 -0
- {teaql-0.2.6 → teaql-0.2.7}/LICENSE +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/setup.cfg +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/__init__.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/core/__init__.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/core/dynamic_search.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/core/entity.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/core/eval.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/core/expr.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/core/graph.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/core/list.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/core/meta.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/core/mutation.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/core/query.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/core/safe_expression.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/core/value.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/core/xls.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/data_service/__init__.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/provider/__init__.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/provider/mysql/__init__.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/provider/mysql/dialect.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/provider/mysql/transport.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/provider/postgres/__init__.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/provider/postgres/dialect.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/provider/postgres/transport.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/provider/redis/__init__.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/provider/redis/remote.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/provider/redis/store.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/provider/sqlite/__init__.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/provider/sqlite/dialect.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/provider/sqlite/transport.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/provider/tfp_client/__init__.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/py.typed +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/runtime/__init__.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/runtime/_schema_capability.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/runtime/builtin-messages-v1.json +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/runtime/env.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/runtime/i18n.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/runtime/module.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/runtime/opentelemetry.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/runtime/store.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/runtime/telemetry.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/runtime/tools.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/runtime/wire_fields.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/sql/__init__.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/sql/dialect.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/sql/executor.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/sql/types.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/web_integration/__init__.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql/web_integration/fastapi/__init__.py +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql.egg-info/dependency_links.txt +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql.egg-info/requires.txt +0 -0
- {teaql-0.2.6 → teaql-0.2.7}/src/teaql.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: teaql
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: TeaQL Runtime and Data Service for Python
|
|
5
5
|
Author: TeaQL
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -39,6 +39,28 @@ Dynamic: license-file
|
|
|
39
39
|
|
|
40
40
|
# TeaQL Python SDK
|
|
41
41
|
|
|
42
|
+
## Sensitive log data
|
|
43
|
+
|
|
44
|
+
Runtime diagnostic logs redact payload values by default, before delivery to
|
|
45
|
+
file, console, buffers, or custom logging sinks. Selecting a diagnostic sink
|
|
46
|
+
alone does not authorize plaintext. For controlled troubleshooting only:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
export TEAQL_ALLOW_SENSITIVE_PLAINTEXT_LOGS=I_UNDERSTAND_SENSITIVE_DATA_MAY_BE_WRITTEN_TO_DISK
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Only this exact value enables plaintext permission; empty values, `true`, and
|
|
53
|
+
whitespace variants do not. Enabling it emits a warning. Credential-classified
|
|
54
|
+
fields remain redacted. The flag does not force every sink to expose values.
|
|
55
|
+
SQL without reliable field/literal provenance may be suppressed and marked
|
|
56
|
+
`NOT REPLAYABLE`. Execution parameters and persisted business data are unchanged.
|
|
57
|
+
|
|
58
|
+
Do not put sensitive data in free-text comments or purpose declarations.
|
|
59
|
+
TeaQL cannot govern arbitrary application prints or independent driver loggers;
|
|
60
|
+
configure those separately. This setting does not erase older plaintext files.
|
|
61
|
+
Restrict access and retention when using plaintext diagnostics, then unset the
|
|
62
|
+
variable and restart processes when troubleshooting is complete.
|
|
63
|
+
|
|
42
64
|
TeaQL Python SDK is a runtime engine and toolkit for building data-driven business applications. It provides seamless integration with the TeaQL ecosystem, fully aligned with the `teaql-rs` baseline.
|
|
43
65
|
|
|
44
66
|
## Recommended Agent Harness
|
|
@@ -100,5 +122,26 @@ orders = await client.execute_query(FederalQuery(
|
|
|
100
122
|
await client.aclose()
|
|
101
123
|
```
|
|
102
124
|
|
|
125
|
+
## Security Foundation Status
|
|
126
|
+
|
|
127
|
+
Python currently provides governed local SQL execution and a **TFP client**;
|
|
128
|
+
it does not claim a public TFP server endpoint. Application queries retain
|
|
129
|
+
non-empty comment/purpose and audited mutations retain their audit reason.
|
|
130
|
+
Runtime logging should keep parameterized SQL and intent separate from any
|
|
131
|
+
restricted value-bearing diagnostic output.
|
|
132
|
+
|
|
133
|
+
Portable `UserContext` opaque entity references are not implemented in the
|
|
134
|
+
Python runtime yet. Until that capability is added, applications must not
|
|
135
|
+
invent a Python-specific token format or serialize internal ID/version pairs as
|
|
136
|
+
if they were the TeaQL portable contract. A Python TFP client may carry an
|
|
137
|
+
opaque token issued by a trusted Java, Rust, Go, or .NET backend, but it must
|
|
138
|
+
not decode, rewrite, or mint that token.
|
|
139
|
+
|
|
140
|
+
The planned wire format, fail-closed behavior, shared golden vector, and exact
|
|
141
|
+
development-only acknowledgement are maintained in the canonical
|
|
142
|
+
[opaque entity reference contract](https://github.com/teaql/teaql-conformance/blob/main/design/opaque-entity-references.md).
|
|
143
|
+
Opaque tokens never replace the backend's authorization, tenant, ownership,
|
|
144
|
+
role, or optimistic-version checks.
|
|
145
|
+
|
|
103
146
|
---
|
|
104
147
|
To run test validations and business logic simulations locally, simply run `pytest` in the project root.
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# TeaQL Python SDK
|
|
2
2
|
|
|
3
|
+
## Sensitive log data
|
|
4
|
+
|
|
5
|
+
Runtime diagnostic logs redact payload values by default, before delivery to
|
|
6
|
+
file, console, buffers, or custom logging sinks. Selecting a diagnostic sink
|
|
7
|
+
alone does not authorize plaintext. For controlled troubleshooting only:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
export TEAQL_ALLOW_SENSITIVE_PLAINTEXT_LOGS=I_UNDERSTAND_SENSITIVE_DATA_MAY_BE_WRITTEN_TO_DISK
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Only this exact value enables plaintext permission; empty values, `true`, and
|
|
14
|
+
whitespace variants do not. Enabling it emits a warning. Credential-classified
|
|
15
|
+
fields remain redacted. The flag does not force every sink to expose values.
|
|
16
|
+
SQL without reliable field/literal provenance may be suppressed and marked
|
|
17
|
+
`NOT REPLAYABLE`. Execution parameters and persisted business data are unchanged.
|
|
18
|
+
|
|
19
|
+
Do not put sensitive data in free-text comments or purpose declarations.
|
|
20
|
+
TeaQL cannot govern arbitrary application prints or independent driver loggers;
|
|
21
|
+
configure those separately. This setting does not erase older plaintext files.
|
|
22
|
+
Restrict access and retention when using plaintext diagnostics, then unset the
|
|
23
|
+
variable and restart processes when troubleshooting is complete.
|
|
24
|
+
|
|
3
25
|
TeaQL Python SDK is a runtime engine and toolkit for building data-driven business applications. It provides seamless integration with the TeaQL ecosystem, fully aligned with the `teaql-rs` baseline.
|
|
4
26
|
|
|
5
27
|
## Recommended Agent Harness
|
|
@@ -61,5 +83,26 @@ orders = await client.execute_query(FederalQuery(
|
|
|
61
83
|
await client.aclose()
|
|
62
84
|
```
|
|
63
85
|
|
|
86
|
+
## Security Foundation Status
|
|
87
|
+
|
|
88
|
+
Python currently provides governed local SQL execution and a **TFP client**;
|
|
89
|
+
it does not claim a public TFP server endpoint. Application queries retain
|
|
90
|
+
non-empty comment/purpose and audited mutations retain their audit reason.
|
|
91
|
+
Runtime logging should keep parameterized SQL and intent separate from any
|
|
92
|
+
restricted value-bearing diagnostic output.
|
|
93
|
+
|
|
94
|
+
Portable `UserContext` opaque entity references are not implemented in the
|
|
95
|
+
Python runtime yet. Until that capability is added, applications must not
|
|
96
|
+
invent a Python-specific token format or serialize internal ID/version pairs as
|
|
97
|
+
if they were the TeaQL portable contract. A Python TFP client may carry an
|
|
98
|
+
opaque token issued by a trusted Java, Rust, Go, or .NET backend, but it must
|
|
99
|
+
not decode, rewrite, or mint that token.
|
|
100
|
+
|
|
101
|
+
The planned wire format, fail-closed behavior, shared golden vector, and exact
|
|
102
|
+
development-only acknowledgement are maintained in the canonical
|
|
103
|
+
[opaque entity reference contract](https://github.com/teaql/teaql-conformance/blob/main/design/opaque-entity-references.md).
|
|
104
|
+
Opaque tokens never replace the backend's authorization, tenant, ownership,
|
|
105
|
+
role, or optimistic-version checks.
|
|
106
|
+
|
|
64
107
|
---
|
|
65
108
|
To run test validations and business logic simulations locally, simply run `pytest` in the project root.
|
|
@@ -29,19 +29,28 @@ class RawAuditEvent:
|
|
|
29
29
|
category: Optional[str] = None
|
|
30
30
|
|
|
31
31
|
def safe(self, mask_fields: List[str], max_length: Optional[int]) -> "SafeAuditEvent":
|
|
32
|
+
from .log_privacy import REDACTED, credential_name, payload_has_credentials, plaintext_enabled, scrub, value_strings
|
|
33
|
+
allow = plaintext_enabled()
|
|
34
|
+
secrets = []
|
|
32
35
|
fields = []
|
|
33
36
|
for change in self.changes:
|
|
34
37
|
value = None if change.new_value is None else str(getattr(change.new_value, "val", change.new_value))
|
|
35
|
-
masked = change.field
|
|
38
|
+
masked = (credential_name(change.field)
|
|
39
|
+
or payload_has_credentials(change.new_value)
|
|
40
|
+
or payload_has_credentials(change.old_value)
|
|
41
|
+
or (change.field in mask_fields and not allow))
|
|
42
|
+
if masked:
|
|
43
|
+
secrets.extend(value_strings(change.old_value))
|
|
44
|
+
secrets.extend(value_strings(change.new_value))
|
|
36
45
|
if value is not None and masked:
|
|
37
|
-
value =
|
|
46
|
+
value = REDACTED
|
|
38
47
|
truncated = value is not None and max_length is not None and len(value) > max_length
|
|
39
48
|
if truncated:
|
|
40
49
|
value = "*" * max_length if max_length <= 3 else value[:max_length - 3] + "..."
|
|
41
50
|
fields.append(SafeAuditField(change.field, value, masked, truncated))
|
|
42
51
|
return SafeAuditEvent(
|
|
43
|
-
self.kind, self.entity, self.entity_id, tuple(fields), self.trace_chain,
|
|
44
|
-
self.actor, self.category,
|
|
52
|
+
self.kind, self.entity, self.entity_id, scrub(tuple(fields), secrets), scrub(self.trace_chain, secrets),
|
|
53
|
+
scrub(self.actor, secrets), self.category,
|
|
45
54
|
)
|
|
46
55
|
|
|
47
56
|
|
|
@@ -739,6 +739,8 @@ class UserContext:
|
|
|
739
739
|
elif entry.affected_rows is not None:
|
|
740
740
|
entry.result_summary = f"{entry.affected_rows} rows affected"
|
|
741
741
|
|
|
742
|
+
from .log_privacy import sql_log_projection
|
|
743
|
+
entry = sql_log_projection(entry)
|
|
742
744
|
logs = self.sql_logs()
|
|
743
745
|
logs.append(entry)
|
|
744
746
|
self._resources["sql_logs"] = logs
|
|
@@ -750,7 +752,7 @@ class UserContext:
|
|
|
750
752
|
buf.entries.append(UnifiedLogEntry(
|
|
751
753
|
timestamp=entry.started_at,
|
|
752
754
|
user_identifier=self.user_identifier(),
|
|
753
|
-
trace_chain=
|
|
755
|
+
trace_chain=entry.trace_path,
|
|
754
756
|
payload=LogPayload.Sql(entry)
|
|
755
757
|
))
|
|
756
758
|
|
|
@@ -784,6 +786,8 @@ class UserContext:
|
|
|
784
786
|
elif affected_rows is not None:
|
|
785
787
|
entry.result_summary = f"{affected_rows} rows affected"
|
|
786
788
|
|
|
789
|
+
from .log_privacy import sql_log_projection
|
|
790
|
+
entry = sql_log_projection(entry)
|
|
787
791
|
logs = self.sql_logs()
|
|
788
792
|
logs.append(entry)
|
|
789
793
|
self._resources["sql_logs"] = logs
|
|
@@ -1033,7 +1037,7 @@ class SqlLogEntry:
|
|
|
1033
1037
|
result_summary: str
|
|
1034
1038
|
|
|
1035
1039
|
class DiagnosticSqlLogSink:
|
|
1036
|
-
"""
|
|
1040
|
+
"""Policy-projected SQL destination; the text sink is installed by default."""
|
|
1037
1041
|
def write(self, entry: SqlLogEntry) -> None:
|
|
1038
1042
|
raise NotImplementedError
|
|
1039
1043
|
|
|
@@ -1042,6 +1046,8 @@ class TextDiagnosticSqlLogSink(DiagnosticSqlLogSink):
|
|
|
1042
1046
|
self._writer = writer
|
|
1043
1047
|
|
|
1044
1048
|
def write(self, entry: SqlLogEntry) -> None:
|
|
1049
|
+
from .log_privacy import sql_log_projection
|
|
1050
|
+
entry = sql_log_projection(entry)
|
|
1045
1051
|
elapsed_us = int(entry.elapsed.total_seconds() * 1_000_000) if entry.elapsed else 0
|
|
1046
1052
|
self._writer(
|
|
1047
1053
|
f"[TeaQL SQL][{entry.operation.name.lower()}][{elapsed_us}us] "
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Log-only projections. Never modify execution parameters or stored values."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
from dataclasses import fields, is_dataclass, replace
|
|
7
|
+
from functools import lru_cache
|
|
8
|
+
|
|
9
|
+
PLAINTEXT_ENV = "TEAQL_ALLOW_SENSITIVE_PLAINTEXT_LOGS"
|
|
10
|
+
PLAINTEXT_ACK = "I_UNDERSTAND_SENSITIVE_DATA_MAY_BE_WRITTEN_TO_DISK"
|
|
11
|
+
REDACTED = "[REDACTED]"
|
|
12
|
+
SQL_REDACTED = "[REDACTED SQL; NOT REPLAYABLE]"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@lru_cache(maxsize=1)
|
|
16
|
+
def _warn_plaintext():
|
|
17
|
+
logging.getLogger("teaql.runtime").warning(
|
|
18
|
+
"Sensitive plaintext logging enabled: application data may be written to disk. "
|
|
19
|
+
"Authentication secrets remain redacted."
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def plaintext_enabled():
|
|
24
|
+
enabled = os.environ.get(PLAINTEXT_ENV) == PLAINTEXT_ACK
|
|
25
|
+
if enabled:
|
|
26
|
+
_warn_plaintext()
|
|
27
|
+
return enabled
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def credential_name(name):
|
|
31
|
+
normalized = re.sub(r"[^a-z0-9]", "", str(name).lower())
|
|
32
|
+
return any(word in normalized for word in (
|
|
33
|
+
"password", "passwd", "passphrase", "privatekey", "secret", "accesstoken",
|
|
34
|
+
"refreshtoken", "idtoken", "apikey", "authorization", "credential",
|
|
35
|
+
"sessiontoken", "magiclinktoken",
|
|
36
|
+
))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def payload_has_credentials(value):
|
|
40
|
+
value = getattr(value, "val", value)
|
|
41
|
+
if isinstance(value, dict):
|
|
42
|
+
return any(credential_name(k) or payload_has_credentials(v) for k, v in value.items())
|
|
43
|
+
if isinstance(value, (tuple, list)):
|
|
44
|
+
return any(payload_has_credentials(v) for v in value)
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def value_strings(value):
|
|
49
|
+
value = getattr(value, "val", value)
|
|
50
|
+
if isinstance(value, dict):
|
|
51
|
+
return [s for v in value.values() for s in value_strings(v)]
|
|
52
|
+
if isinstance(value, (tuple, list)):
|
|
53
|
+
return [s for v in value for s in value_strings(v)]
|
|
54
|
+
return [] if value is None or value == "" else [str(value)]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def scrub(value, secrets):
|
|
58
|
+
"""Copy annotations, removing known values even when embedded in intent."""
|
|
59
|
+
if isinstance(value, str):
|
|
60
|
+
for secret in sorted(set(secrets), key=len, reverse=True):
|
|
61
|
+
value = value.replace(secret, REDACTED)
|
|
62
|
+
return value
|
|
63
|
+
if isinstance(value, list):
|
|
64
|
+
return [scrub(v, secrets) for v in value]
|
|
65
|
+
if isinstance(value, tuple):
|
|
66
|
+
return tuple(scrub(v, secrets) for v in value)
|
|
67
|
+
if isinstance(value, dict):
|
|
68
|
+
return {k: scrub(v, secrets) for k, v in value.items()}
|
|
69
|
+
if is_dataclass(value):
|
|
70
|
+
return replace(value, **{f.name: scrub(getattr(value, f.name), secrets)
|
|
71
|
+
for f in fields(value) if f.init})
|
|
72
|
+
return value
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def sql_log_projection(entry):
|
|
76
|
+
# SQL has no per-parameter field provenance. A credential-bearing query
|
|
77
|
+
# therefore suppresses its whole payload, including on debug opt-in.
|
|
78
|
+
credentials = (credential_name(entry.sql) or credential_name(entry.debug_sql)
|
|
79
|
+
or payload_has_credentials(entry.params))
|
|
80
|
+
allow = plaintext_enabled() and not credentials
|
|
81
|
+
if allow:
|
|
82
|
+
return replace(entry, params=list(entry.params), trace_path=list(entry.trace_path))
|
|
83
|
+
secrets = value_strings(entry.params)
|
|
84
|
+
sql = entry.sql
|
|
85
|
+
# Arbitrary literal SQL is not safely redactable across dialects. Preserve
|
|
86
|
+
# parameterized shape only when there are no literals/comments to expose.
|
|
87
|
+
if any(token in sql for token in ("'", '"', "`", "--", "/*", "$")) or re.search(r"\b\d+\b", sql):
|
|
88
|
+
sql = SQL_REDACTED
|
|
89
|
+
return replace(entry, sql=scrub(sql, secrets), params=[None] * len(entry.params),
|
|
90
|
+
debug_sql=SQL_REDACTED, pretty_sql=SQL_REDACTED,
|
|
91
|
+
comment=scrub(entry.comment, secrets), purpose=scrub(entry.purpose, secrets),
|
|
92
|
+
audit_reason=scrub(entry.audit_reason, secrets),
|
|
93
|
+
trace_path=scrub(entry.trace_path, secrets))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: teaql
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: TeaQL Runtime and Data Service for Python
|
|
5
5
|
Author: TeaQL
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -39,6 +39,28 @@ Dynamic: license-file
|
|
|
39
39
|
|
|
40
40
|
# TeaQL Python SDK
|
|
41
41
|
|
|
42
|
+
## Sensitive log data
|
|
43
|
+
|
|
44
|
+
Runtime diagnostic logs redact payload values by default, before delivery to
|
|
45
|
+
file, console, buffers, or custom logging sinks. Selecting a diagnostic sink
|
|
46
|
+
alone does not authorize plaintext. For controlled troubleshooting only:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
export TEAQL_ALLOW_SENSITIVE_PLAINTEXT_LOGS=I_UNDERSTAND_SENSITIVE_DATA_MAY_BE_WRITTEN_TO_DISK
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Only this exact value enables plaintext permission; empty values, `true`, and
|
|
53
|
+
whitespace variants do not. Enabling it emits a warning. Credential-classified
|
|
54
|
+
fields remain redacted. The flag does not force every sink to expose values.
|
|
55
|
+
SQL without reliable field/literal provenance may be suppressed and marked
|
|
56
|
+
`NOT REPLAYABLE`. Execution parameters and persisted business data are unchanged.
|
|
57
|
+
|
|
58
|
+
Do not put sensitive data in free-text comments or purpose declarations.
|
|
59
|
+
TeaQL cannot govern arbitrary application prints or independent driver loggers;
|
|
60
|
+
configure those separately. This setting does not erase older plaintext files.
|
|
61
|
+
Restrict access and retention when using plaintext diagnostics, then unset the
|
|
62
|
+
variable and restart processes when troubleshooting is complete.
|
|
63
|
+
|
|
42
64
|
TeaQL Python SDK is a runtime engine and toolkit for building data-driven business applications. It provides seamless integration with the TeaQL ecosystem, fully aligned with the `teaql-rs` baseline.
|
|
43
65
|
|
|
44
66
|
## Recommended Agent Harness
|
|
@@ -100,5 +122,26 @@ orders = await client.execute_query(FederalQuery(
|
|
|
100
122
|
await client.aclose()
|
|
101
123
|
```
|
|
102
124
|
|
|
125
|
+
## Security Foundation Status
|
|
126
|
+
|
|
127
|
+
Python currently provides governed local SQL execution and a **TFP client**;
|
|
128
|
+
it does not claim a public TFP server endpoint. Application queries retain
|
|
129
|
+
non-empty comment/purpose and audited mutations retain their audit reason.
|
|
130
|
+
Runtime logging should keep parameterized SQL and intent separate from any
|
|
131
|
+
restricted value-bearing diagnostic output.
|
|
132
|
+
|
|
133
|
+
Portable `UserContext` opaque entity references are not implemented in the
|
|
134
|
+
Python runtime yet. Until that capability is added, applications must not
|
|
135
|
+
invent a Python-specific token format or serialize internal ID/version pairs as
|
|
136
|
+
if they were the TeaQL portable contract. A Python TFP client may carry an
|
|
137
|
+
opaque token issued by a trusted Java, Rust, Go, or .NET backend, but it must
|
|
138
|
+
not decode, rewrite, or mint that token.
|
|
139
|
+
|
|
140
|
+
The planned wire format, fail-closed behavior, shared golden vector, and exact
|
|
141
|
+
development-only acknowledgement are maintained in the canonical
|
|
142
|
+
[opaque entity reference contract](https://github.com/teaql/teaql-conformance/blob/main/design/opaque-entity-references.md).
|
|
143
|
+
Opaque tokens never replace the backend's authorization, tenant, ownership,
|
|
144
|
+
role, or optimistic-version checks.
|
|
145
|
+
|
|
103
146
|
---
|
|
104
147
|
To run test validations and business logic simulations locally, simply run `pytest` in the project root.
|
|
@@ -43,6 +43,7 @@ src/teaql/runtime/builtin-messages-v1.json
|
|
|
43
43
|
src/teaql/runtime/context.py
|
|
44
44
|
src/teaql/runtime/env.py
|
|
45
45
|
src/teaql/runtime/i18n.py
|
|
46
|
+
src/teaql/runtime/log_privacy.py
|
|
46
47
|
src/teaql/runtime/module.py
|
|
47
48
|
src/teaql/runtime/opentelemetry.py
|
|
48
49
|
src/teaql/runtime/store.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|