treechain-adapters 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.
@@ -0,0 +1,12 @@
1
+ Proprietary License
2
+ Copyright (c) 2026 TreeChain Labs
3
+ Patent Pending — EP26025007.1
4
+
5
+ All rights reserved. This software and associated documentation
6
+ files are proprietary and confidential. Unauthorized copying,
7
+ modification, distribution, or use of this software, in whole
8
+ or in part, is strictly prohibited without explicit written
9
+ permission from TreeChain Labs.
10
+
11
+ For licensing inquiries: security@treechain.ai
12
+ For more information: treechain.ai
@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.4
2
+ Name: treechain-adapters
3
+ Version: 1.0.0
4
+ Summary: TreeChain database adapters — drop-in encryption for MongoDB, PostgreSQL, MySQL, Redis, SQLite, Supabase. Patent Pending — EP26025007.1
5
+ Author-email: TreeChain Labs <security@treechain.ai>
6
+ License: Proprietary
7
+ Project-URL: Homepage, https://treechain.ai
8
+ Project-URL: Documentation, https://api-eu.treechain.ai/docs
9
+ Keywords: encryption,database,mongodb,postgresql,mysql,redis,steganography,hipaa,zero-knowledge
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: Other/Proprietary License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Database
20
+ Classifier: Topic :: Security :: Cryptography
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: treechain>=4.0.0
25
+ Requires-Dist: httpx>=0.24.0
26
+ Provides-Extra: mongodb
27
+ Requires-Dist: motor>=3.0.0; extra == "mongodb"
28
+ Provides-Extra: postgresql
29
+ Requires-Dist: asyncpg>=0.27.0; extra == "postgresql"
30
+ Provides-Extra: mysql
31
+ Requires-Dist: aiomysql>=0.1.1; extra == "mysql"
32
+ Provides-Extra: sqlite
33
+ Requires-Dist: aiosqlite>=0.19.0; extra == "sqlite"
34
+ Provides-Extra: redis
35
+ Requires-Dist: redis>=5.0.0; extra == "redis"
36
+ Provides-Extra: supabase
37
+ Requires-Dist: supabase>=1.0.0; extra == "supabase"
38
+ Provides-Extra: all
39
+ Requires-Dist: treechain-adapters[mongodb,mysql,postgresql,redis,sqlite,supabase]; extra == "all"
40
+ Provides-Extra: dev
41
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
42
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
43
+ Dynamic: license-file
44
+
45
+ # TreeChain Database Adapters
46
+
47
+ **Patent Pending — EP26025007.1**
48
+
49
+ Drop-in encryption for any database. Three lines of code.
50
+ Every field auto-encrypts with the Polyglottal Cipher.
51
+ Your database stays exactly as is — TreeChain wraps it transparently.
52
+
53
+ ## Supported Databases
54
+
55
+ - **MongoDB** (via Motor async client)
56
+ - **PostgreSQL** (via asyncpg)
57
+ - **MySQL** (via aiomysql)
58
+ - **SQLite** (via aiosqlite)
59
+ - **Redis** (via redis-py async)
60
+ - **Supabase** (via supabase-py)
61
+
62
+ ## Installation
63
+
64
+ ```bash
65
+ pip install treechain-adapters
66
+
67
+ # With specific database support:
68
+ pip install treechain-adapters[mongodb]
69
+ pip install treechain-adapters[postgresql]
70
+ pip install treechain-adapters[all]
71
+ ```
72
+
73
+ ## Usage
74
+
75
+ ```python
76
+ # Before TreeChain:
77
+ client = AsyncIOMotorClient(MONGO_URI)
78
+ db = client.mydb
79
+
80
+ # After TreeChain (3 lines changed):
81
+ from treechain_adapters import MongoDBAdapter
82
+
83
+ tc = MongoDBAdapter(api_key="gj_live_...", customer_id="jordan")
84
+ db = tc.wrap(client.mydb)
85
+
86
+ # Everything else stays identical.
87
+ # Every write auto-encrypts via GlyphJammer.
88
+ # Every read auto-decrypts transparently.
89
+ # Portal populates in real time.
90
+ await db.patients.insert_one({"name": "John Smith", "ssn": "123-45-6789"})
91
+ # Stored as: {"name": "馏𜽡㌎𬵃...", "ssn": "𘫙𡢽甎똂..."}
92
+ ```
93
+
94
+ ## What Happens Under the Hood
95
+
96
+ 1. You write data to your database as normal
97
+ 2. The adapter intercepts string fields and encrypts them with GlyphJammer
98
+ 3. Your database stores multilingual poetry instead of plaintext
99
+ 4. On read, fields are transparently decrypted back
100
+ 5. Your TreeChain portal shows all records in real time
101
+ 6. DuckDB local copy created for redundancy
102
+
103
+ ## Performance
104
+
105
+ - Zero blocking on writes — encryption runs in-line, portal population is fire-and-forget
106
+ - 68ms average encrypt latency on primary node
107
+ - Your application code does not change
108
+
109
+ ## Links
110
+
111
+ - Website: [treechain.ai](https://treechain.ai)
112
+ - API Docs: [api-eu.treechain.ai/docs](https://api-eu.treechain.ai/docs)
113
+ - Support: security@treechain.ai
114
+
115
+ ## License
116
+
117
+ Proprietary — TreeChain Labs
118
+ Patent Pending — EP26025007.1
119
+
120
+ See [LICENSE](LICENSE) for full terms.
@@ -0,0 +1,76 @@
1
+ # TreeChain Database Adapters
2
+
3
+ **Patent Pending — EP26025007.1**
4
+
5
+ Drop-in encryption for any database. Three lines of code.
6
+ Every field auto-encrypts with the Polyglottal Cipher.
7
+ Your database stays exactly as is — TreeChain wraps it transparently.
8
+
9
+ ## Supported Databases
10
+
11
+ - **MongoDB** (via Motor async client)
12
+ - **PostgreSQL** (via asyncpg)
13
+ - **MySQL** (via aiomysql)
14
+ - **SQLite** (via aiosqlite)
15
+ - **Redis** (via redis-py async)
16
+ - **Supabase** (via supabase-py)
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ pip install treechain-adapters
22
+
23
+ # With specific database support:
24
+ pip install treechain-adapters[mongodb]
25
+ pip install treechain-adapters[postgresql]
26
+ pip install treechain-adapters[all]
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ```python
32
+ # Before TreeChain:
33
+ client = AsyncIOMotorClient(MONGO_URI)
34
+ db = client.mydb
35
+
36
+ # After TreeChain (3 lines changed):
37
+ from treechain_adapters import MongoDBAdapter
38
+
39
+ tc = MongoDBAdapter(api_key="gj_live_...", customer_id="jordan")
40
+ db = tc.wrap(client.mydb)
41
+
42
+ # Everything else stays identical.
43
+ # Every write auto-encrypts via GlyphJammer.
44
+ # Every read auto-decrypts transparently.
45
+ # Portal populates in real time.
46
+ await db.patients.insert_one({"name": "John Smith", "ssn": "123-45-6789"})
47
+ # Stored as: {"name": "馏𜽡㌎𬵃...", "ssn": "𘫙𡢽甎똂..."}
48
+ ```
49
+
50
+ ## What Happens Under the Hood
51
+
52
+ 1. You write data to your database as normal
53
+ 2. The adapter intercepts string fields and encrypts them with GlyphJammer
54
+ 3. Your database stores multilingual poetry instead of plaintext
55
+ 4. On read, fields are transparently decrypted back
56
+ 5. Your TreeChain portal shows all records in real time
57
+ 6. DuckDB local copy created for redundancy
58
+
59
+ ## Performance
60
+
61
+ - Zero blocking on writes — encryption runs in-line, portal population is fire-and-forget
62
+ - 68ms average encrypt latency on primary node
63
+ - Your application code does not change
64
+
65
+ ## Links
66
+
67
+ - Website: [treechain.ai](https://treechain.ai)
68
+ - API Docs: [api-eu.treechain.ai/docs](https://api-eu.treechain.ai/docs)
69
+ - Support: security@treechain.ai
70
+
71
+ ## License
72
+
73
+ Proprietary — TreeChain Labs
74
+ Patent Pending — EP26025007.1
75
+
76
+ See [LICENSE](LICENSE) for full terms.
@@ -0,0 +1,65 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "treechain-adapters"
7
+ version = "1.0.0"
8
+ description = "TreeChain database adapters — drop-in encryption for MongoDB, PostgreSQL, MySQL, Redis, SQLite, Supabase. Patent Pending — EP26025007.1"
9
+ readme = "README.md"
10
+ license = {text = "Proprietary"}
11
+ authors = [
12
+ {name = "TreeChain Labs", email = "security@treechain.ai"}
13
+ ]
14
+ keywords = [
15
+ "encryption",
16
+ "database",
17
+ "mongodb",
18
+ "postgresql",
19
+ "mysql",
20
+ "redis",
21
+ "steganography",
22
+ "hipaa",
23
+ "zero-knowledge",
24
+ ]
25
+ classifiers = [
26
+ "Development Status :: 4 - Beta",
27
+ "Intended Audience :: Developers",
28
+ "License :: Other/Proprietary License",
29
+ "Operating System :: OS Independent",
30
+ "Programming Language :: Python :: 3",
31
+ "Programming Language :: Python :: 3.9",
32
+ "Programming Language :: Python :: 3.10",
33
+ "Programming Language :: Python :: 3.11",
34
+ "Programming Language :: Python :: 3.12",
35
+ "Topic :: Database",
36
+ "Topic :: Security :: Cryptography",
37
+ ]
38
+ requires-python = ">=3.9"
39
+ dependencies = [
40
+ "treechain>=4.0.0",
41
+ "httpx>=0.24.0",
42
+ ]
43
+
44
+ [project.optional-dependencies]
45
+ mongodb = ["motor>=3.0.0"]
46
+ postgresql = ["asyncpg>=0.27.0"]
47
+ mysql = ["aiomysql>=0.1.1"]
48
+ sqlite = ["aiosqlite>=0.19.0"]
49
+ redis = ["redis>=5.0.0"]
50
+ supabase = ["supabase>=1.0.0"]
51
+ all = [
52
+ "treechain-adapters[mongodb,postgresql,mysql,sqlite,redis,supabase]",
53
+ ]
54
+ dev = [
55
+ "pytest>=7.0.0",
56
+ "pytest-asyncio>=0.21.0",
57
+ ]
58
+
59
+ [project.urls]
60
+ Homepage = "https://treechain.ai"
61
+ Documentation = "https://api-eu.treechain.ai/docs"
62
+
63
+ [tool.setuptools.packages.find]
64
+ where = ["src"]
65
+ include = ["treechain_adapters*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,68 @@
1
+ """
2
+ TreeChain [DATABASE_NAME] Adapter — TEMPLATE
3
+
4
+ Copy this file and implement 3 things:
5
+ 1. Wrap the client class to intercept writes
6
+ 2. Call self._adapter._auto_populate() on every write
7
+ 3. Increment self._adapter._write_count / _read_count
8
+
9
+ Time to implement: ~20 minutes for a new database.
10
+
11
+ Steps:
12
+ 1. Copy this file as yourdb_adapter.py
13
+ 2. Replace _WrappedClient with your DB's client wrapper
14
+ 3. Intercept the write methods (insert, execute, set, etc.)
15
+ 4. Call _auto_populate with the data and a source name
16
+ 5. Add to __init__.py exports
17
+ 6. Test with: python -m pytest sdk/tests/test_adapters.py
18
+ """
19
+
20
+ from typing import Any, Dict
21
+ from .base import TreeChainDBAdapter
22
+
23
+
24
+ class _WrappedClient:
25
+ """Wrap the database client. Intercept writes, pass through reads."""
26
+
27
+ def __init__(self, client, adapter: 'YourDBAdapter'):
28
+ self._client = client
29
+ self._adapter = adapter
30
+
31
+ async def your_write_method(self, *args, **kwargs):
32
+ """Replace with the actual write method name (insert, execute, set, etc.)"""
33
+ # 1. Call the original method first — never block the customer
34
+ result = await self._client.your_write_method(*args, **kwargs)
35
+
36
+ # 2. Increment write counter
37
+ self._adapter._write_count += 1
38
+
39
+ # 3. Auto-populate portal (fire-and-forget, never throws)
40
+ await self._adapter._auto_populate(
41
+ {"data": str(args)[:500]}, # Extract meaningful fields from args
42
+ "your_table_writes", # Collection name in portal
43
+ )
44
+
45
+ # 4. Return original result — customer sees no difference
46
+ return result
47
+
48
+ async def your_read_method(self, *args, **kwargs):
49
+ """Reads just pass through with a counter increment."""
50
+ self._adapter._read_count += 1
51
+ return await self._client.your_read_method(*args, **kwargs)
52
+
53
+ def __getattr__(self, name):
54
+ """Pass through anything we don't intercept."""
55
+ return getattr(self._client, name)
56
+
57
+
58
+ class YourDBAdapter(TreeChainDBAdapter):
59
+ """
60
+ [DATABASE_NAME] adapter for TreeChain.
61
+
62
+ Usage:
63
+ tc = YourDBAdapter(api_key="gj_live_...", customer_id="jordan")
64
+ client = tc.wrap(your_db_client)
65
+ """
66
+
67
+ def wrap(self, client) -> _WrappedClient:
68
+ return _WrappedClient(client, self)
@@ -0,0 +1,30 @@
1
+ """
2
+ TreeChain Database Adapters
3
+
4
+ Drop-in wrappers for every major database. 3 lines to integrate:
5
+
6
+ from treechain.db_adapters import MongoDBAdapter
7
+ tc = MongoDBAdapter(api_key="gj_live_...", customer_id="jordan")
8
+ db = tc.wrap(your_existing_db_client)
9
+
10
+ Every write auto-encrypts and populates your TreeChain portal.
11
+ Zero latency impact. Zero code changes beyond the 3 lines above.
12
+ """
13
+
14
+ from .base import TreeChainDBAdapter
15
+ from .mongodb_adapter import MongoDBAdapter
16
+ from .postgresql_adapter import PostgreSQLAdapter
17
+ from .mysql_adapter import MySQLAdapter
18
+ from .sqlite_adapter import SQLiteAdapter
19
+ from .redis_adapter import RedisAdapter
20
+ from .supabase_adapter import SupabaseAdapter
21
+
22
+ __all__ = [
23
+ "TreeChainDBAdapter",
24
+ "MongoDBAdapter",
25
+ "PostgreSQLAdapter",
26
+ "MySQLAdapter",
27
+ "SQLiteAdapter",
28
+ "RedisAdapter",
29
+ "SupabaseAdapter",
30
+ ]
@@ -0,0 +1,214 @@
1
+ """
2
+ TreeChain DB Adapter — Base Class
3
+
4
+ Every database adapter inherits from this. Intercept writes,
5
+ encrypt fields via GlyphJammer, auto-populate the customer portal
6
+ in real time. Zero latency impact. Zero code change for the customer.
7
+
8
+ Usage:
9
+ from treechain.db_adapters import MongoDBAdapter
10
+ tc = MongoDBAdapter(api_key="gj_live_...", customer_id="jordan")
11
+ db = tc.wrap(client.mydb)
12
+ # Everything else stays identical — every write auto-populates
13
+ """
14
+
15
+ import asyncio
16
+ import hashlib
17
+ import json
18
+ import logging
19
+ import os
20
+ import threading
21
+ import time
22
+ from abc import ABC, abstractmethod
23
+ from datetime import datetime, timezone
24
+ from typing import Any, Dict, List, Optional
25
+
26
+ log = logging.getLogger("treechain.db_adapter")
27
+
28
+
29
+ class TreeChainDBAdapter(ABC):
30
+ """
31
+ Base class for all TreeChain database adapters.
32
+
33
+ Drop-in wrapper: intercepts reads/writes, encrypts field values
34
+ via GlyphJammer, and fires async hooks to populate the customer's
35
+ encrypted database portal. The main database operation is never
36
+ blocked or slowed.
37
+ """
38
+
39
+ def __init__(
40
+ self,
41
+ api_key: str = None,
42
+ customer_id: str = None,
43
+ collection_prefix: str = "cdb",
44
+ mongo_uri: str = None,
45
+ master_key: str = None,
46
+ auto_populate: bool = True,
47
+ encrypt_fields: bool = True,
48
+ ):
49
+ self.api_key = api_key
50
+ self.customer_id = customer_id or self._resolve_customer_id(api_key)
51
+ self.collection_prefix = collection_prefix
52
+ self.auto_populate = auto_populate
53
+ self.encrypt_fields = encrypt_fields
54
+ self._mongo_uri = mongo_uri or os.getenv("MONGODB_URI") or os.getenv("MONGO_URI")
55
+ self._master_key = master_key or os.getenv("TREECHAIN_MASTER_KEY")
56
+ self._jammer = None
57
+ self._mongo_client = None
58
+ self._mongo_db = None
59
+ self._duckdb = None
60
+ self._write_count = 0
61
+ self._read_count = 0
62
+ self._populate_count = 0
63
+ self._error_count = 0
64
+
65
+ def _resolve_customer_id(self, api_key: str) -> str:
66
+ """Derive customer_id from API key hash if not provided."""
67
+ if not api_key:
68
+ return "anonymous"
69
+ raw = api_key.strip()
70
+ if raw.startswith("tc_"):
71
+ raw = raw[3:]
72
+ return hashlib.sha256(raw.encode()).hexdigest()[:12]
73
+
74
+ def _get_jammer(self):
75
+ """Lazy-init GlyphJammer."""
76
+ if self._jammer is None and self._master_key:
77
+ try:
78
+ import sys
79
+ for p in [
80
+ os.path.join(os.path.dirname(__file__), "..", "..", "vendor", "treechain_sdk", "treechain-sdk-v4"),
81
+ os.path.join(os.path.dirname(__file__), "..", "..", "treechain-sdk-v4"),
82
+ ]:
83
+ if os.path.exists(p) and p not in sys.path:
84
+ sys.path.insert(0, p)
85
+ from treechain.core.encryption import GlyphJammer
86
+ self._jammer = GlyphJammer(master_key=self._master_key)
87
+ except Exception as e:
88
+ log.warning("GlyphJammer init failed: %s", e)
89
+ return self._jammer
90
+
91
+ def _get_mongo(self):
92
+ """Lazy-init MongoDB client for portal writes."""
93
+ if self._mongo_client is None and self._mongo_uri:
94
+ try:
95
+ from motor.motor_asyncio import AsyncIOMotorClient
96
+ self._mongo_client = AsyncIOMotorClient(
97
+ self._mongo_uri, maxPoolSize=5, minPoolSize=1,
98
+ serverSelectionTimeoutMS=5000,
99
+ )
100
+ self._mongo_db = self._mongo_client[os.getenv("MONGO_DB", "glyphjammer")]
101
+ except Exception as e:
102
+ log.warning("MongoDB portal connection failed: %s", e)
103
+ return self._mongo_db
104
+
105
+ def _get_duckdb(self):
106
+ """Lazy-init DuckDB for local redundancy."""
107
+ if self._duckdb is None:
108
+ try:
109
+ from stores.duckdb_store import DuckDBProvenanceStore
110
+ self._duckdb = DuckDBProvenanceStore(
111
+ master_key=self._master_key,
112
+ node_id=os.getenv("NODE_ID", "unknown"),
113
+ )
114
+ except Exception:
115
+ pass
116
+ return self._duckdb
117
+
118
+ def _ns(self, source: str) -> str:
119
+ """Build namespaced collection name."""
120
+ safe_cust = "".join(c for c in self.customer_id if c.isalnum() or c in ("_", "-"))[:64]
121
+ safe_src = "".join(c for c in source if c.isalnum() or c in ("_", "-"))[:64]
122
+ return f"{self.collection_prefix}_{safe_cust}_{safe_src}"
123
+
124
+ def _encrypt_doc(self, data: Dict) -> tuple:
125
+ """Encrypt all string values in a document. Returns (encrypted_doc, encrypted_fields_meta)."""
126
+ jammer = self._get_jammer()
127
+ if not jammer or not self.encrypt_fields:
128
+ return data, {}
129
+
130
+ doc = {}
131
+ ef = {}
132
+ for k, v in data.items():
133
+ if k.startswith("_"):
134
+ doc[k] = v
135
+ continue
136
+ if isinstance(v, str) and v:
137
+ try:
138
+ result = jammer.encrypt(str(v), emotion="love")
139
+ doc[k] = getattr(result, "glyph_payload", str(result))
140
+ ef[k] = {
141
+ "shield_id": getattr(result, "shield_id", ""),
142
+ "meta": getattr(result, "metadata", {}),
143
+ }
144
+ except Exception:
145
+ doc[k] = v
146
+ else:
147
+ doc[k] = v
148
+ return doc, ef
149
+
150
+ async def _auto_populate(self, data: Dict, source: str, operation: str = "write"):
151
+ """
152
+ Fire-and-forget portal population.
153
+ Never blocks. Never throws. Never slows the customer's operation.
154
+ """
155
+ if not self.auto_populate:
156
+ return
157
+
158
+ try:
159
+ enc_doc, ef = self._encrypt_doc(data)
160
+ enc_doc["_encrypted_fields"] = ef
161
+ enc_doc["_created_at"] = datetime.now(timezone.utc)
162
+ enc_doc["_customer_id"] = self.customer_id
163
+ enc_doc["_source_adapter"] = self.__class__.__name__
164
+ enc_doc["_operation"] = operation
165
+
166
+ ns = self._ns(source)
167
+
168
+ # MongoDB async write
169
+ db = self._get_mongo()
170
+ if db is not None:
171
+ asyncio.ensure_future(db[ns].insert_one(enc_doc))
172
+
173
+ # DuckDB local write
174
+ ddb = self._get_duckdb()
175
+ if ddb:
176
+ ddb.store_async(ns, json.dumps(ef, default=str)[:200], enc_doc)
177
+
178
+ self._populate_count += 1
179
+ except Exception as e:
180
+ self._error_count += 1
181
+ log.debug("Auto-populate failed (non-fatal): %s", e)
182
+
183
+ def _auto_populate_sync(self, data: Dict, source: str, operation: str = "write"):
184
+ """Sync wrapper for auto_populate — used by sync adapters."""
185
+ try:
186
+ loop = asyncio.get_event_loop()
187
+ if loop.is_running():
188
+ asyncio.ensure_future(self._auto_populate(data, source, operation))
189
+ else:
190
+ loop.run_until_complete(self._auto_populate(data, source, operation))
191
+ except RuntimeError:
192
+ # No event loop — fire in background thread
193
+ threading.Thread(
194
+ target=lambda: asyncio.run(self._auto_populate(data, source, operation)),
195
+ daemon=True,
196
+ ).start()
197
+
198
+ def stats(self) -> Dict:
199
+ """Adapter statistics."""
200
+ return {
201
+ "adapter": self.__class__.__name__,
202
+ "customer_id": self.customer_id,
203
+ "writes": self._write_count,
204
+ "reads": self._read_count,
205
+ "populated": self._populate_count,
206
+ "errors": self._error_count,
207
+ "auto_populate": self.auto_populate,
208
+ "encrypt_fields": self.encrypt_fields,
209
+ }
210
+
211
+ @abstractmethod
212
+ def wrap(self, client: Any) -> Any:
213
+ """Wrap the customer's database client. Returns the wrapped client."""
214
+ ...