temporalcodec 0.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.
@@ -0,0 +1,26 @@
1
+ dist
2
+ tmp
3
+
4
+ node_modules
5
+
6
+ .idea
7
+ .DS_Store
8
+ Thumbs.db
9
+ .commit
10
+ .devbox
11
+ .envrc
12
+ go.work.sum
13
+
14
+ ######################
15
+ ## gRPC conventions ##
16
+ ######################
17
+
18
+ # Go-generated files
19
+ **/*.pb.go
20
+ # TypeScript-generated gRPC files
21
+ **/*/src/interfaces
22
+ # Avoid duplication from Docker Compose volumes
23
+ **/*/proto
24
+
25
+ __pycache__
26
+ .venv
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.4
2
+ Name: temporalcodec
3
+ Version: 0.0.1
4
+ Summary: Encode and decode your Temporal data
5
+ Project-URL: Homepage, https://github.com/mrsimonemms/temporal-codec-server
6
+ Project-URL: Repository, https://github.com/mrsimonemms/temporal-codec-server
7
+ Project-URL: Documentation, https://github.com/mrsimonemms/temporal-codec-server
8
+ Project-URL: Bug Tracker, https://github.com/mrsimonemms/temporal-codec-server/issues
9
+ Author-email: Simon Emms <simon@simonemms.com>
10
+ License: Apache-2.0
11
+ Keywords: codec,decoding,decryption,encoding,encryption,temporal,workflow
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+
15
+ # temporal-codec
16
+
17
+ Encode and decode your Temporal data with Python
18
+
19
+ <!-- toc -->
20
+
21
+ * [Temporal SDK example](#temporal-sdk-example)
22
+ * [Installation](#installation)
23
+
24
+ <!-- Regenerate with "pre-commit run -a markdown-toc" -->
25
+
26
+ <!-- tocstop -->
27
+
28
+ ## Temporal SDK example
29
+
30
+ > See [keys.example.yaml](https://github.com/mrsimonemms/temporal-codec-server/blob/e11e08a51b0cc0673363e6df3d4d4280319bce2b/keys.example.yaml)
31
+ > for an example key file.
32
+ >
33
+ > For best results, use an environment variable rather than hardcoding the file
34
+ > path.
35
+
36
+ ### Installation
@@ -0,0 +1,22 @@
1
+ # temporal-codec
2
+
3
+ Encode and decode your Temporal data with Python
4
+
5
+ <!-- toc -->
6
+
7
+ * [Temporal SDK example](#temporal-sdk-example)
8
+ * [Installation](#installation)
9
+
10
+ <!-- Regenerate with "pre-commit run -a markdown-toc" -->
11
+
12
+ <!-- tocstop -->
13
+
14
+ ## Temporal SDK example
15
+
16
+ > See [keys.example.yaml](https://github.com/mrsimonemms/temporal-codec-server/blob/e11e08a51b0cc0673363e6df3d4d4280319bce2b/keys.example.yaml)
17
+ > for an example key file.
18
+ >
19
+ > For best results, use an environment variable rather than hardcoding the file
20
+ > path.
21
+
22
+ ### Installation
@@ -0,0 +1,45 @@
1
+ # Copyright 2025 Simon Emms <simon@simonemms.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ [project]
16
+ name = "temporalcodec"
17
+ version = "0.0.1"
18
+ description = "Encode and decode your Temporal data"
19
+ readme = "README.md"
20
+ authors = [{ name = "Simon Emms", email = "simon@simonemms.com" }]
21
+ requires-python = ">=3.9"
22
+ dependencies = []
23
+ license = { text = "Apache-2.0" }
24
+ keywords = [
25
+ "temporal",
26
+ "workflow",
27
+ "encryption",
28
+ "decryption",
29
+ "codec",
30
+ "encoding",
31
+ "decoding",
32
+ ]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/mrsimonemms/temporal-codec-server"
36
+ Repository = "https://github.com/mrsimonemms/temporal-codec-server"
37
+ Documentation = "https://github.com/mrsimonemms/temporal-codec-server"
38
+ "Bug Tracker" = "https://github.com/mrsimonemms/temporal-codec-server/issues"
39
+
40
+ [build-system]
41
+ requires = ["hatchling"]
42
+ build-backend = "hatchling.build"
43
+
44
+ [tool.hatch.build.targets.wheel]
45
+ packages = ["."]
@@ -0,0 +1,97 @@
1
+ # Copyright 2025 Simon Emms <simon@simonemms.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from typing import Iterable, List, TypedDict
16
+ from dataclasses import dataclass
17
+ import yaml
18
+ import os
19
+ from temporalio.api.common.v1 import Payload
20
+ from temporalio.converter import PayloadCodec
21
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
22
+
23
+ metadata_keyid = "encryption-key-id"
24
+ metadata_encoding = "encoding"
25
+ encoding_type = "binary/encrypted"
26
+
27
+
28
+ @dataclass
29
+ class Key(TypedDict):
30
+ id: str
31
+ key: str
32
+
33
+
34
+ class EncryptionCodec(PayloadCodec):
35
+ def __init__(self, keys: List[Key]) -> None:
36
+ super().__init__()
37
+
38
+ if len(keys) == 0:
39
+ # @todo(sje): this is probably not a TypeError
40
+ raise TypeError(f'Keys are required for AES encryption')
41
+
42
+ self.keys = keys
43
+
44
+ async def decode(self, payloads: Iterable[Payload]) -> List[Payload]:
45
+ ret: List[Payload] = []
46
+ for p in payloads:
47
+ if p.metadata.get(metadata_encoding, b"").decode() != encoding_type:
48
+ ret.append(p)
49
+ continue
50
+
51
+ key_id = p.metadata.get("encryption-key-id", b"").decode()
52
+
53
+ key = None
54
+ for k in self.keys:
55
+ if k.get("id") == key_id:
56
+ key = k.get("key")
57
+
58
+ if key == None:
59
+ raise ValueError(f"Unrecognized key ID {key_id}.")
60
+
61
+ encryptor = AESGCM(key.encode())
62
+ ret.append(Payload.FromString(self.__decode(encryptor, p.data)))
63
+ return ret
64
+
65
+ async def encode(self, payloads: Iterable[Payload]) -> List[Payload]:
66
+ active_key = self.keys[0]
67
+ encryptor = AESGCM(active_key.get("key").encode())
68
+
69
+ return [
70
+ Payload(
71
+ metadata={
72
+ metadata_encoding: encoding_type.encode(),
73
+ metadata_keyid: active_key.get("id").encode(),
74
+ },
75
+ data=self.__encode(encryptor, p.SerializeToString()),
76
+ )
77
+ for p in payloads
78
+ ]
79
+
80
+ @staticmethod
81
+ def __decode(encryptor: AESGCM, data: bytes) -> bytes:
82
+ return encryptor.decrypt(data[:12], data[12:], None)
83
+
84
+ @staticmethod
85
+ def __encode(encryptor: AESGCM, data: bytes) -> bytes:
86
+ nonce = os.urandom(12)
87
+ return nonce + encryptor.encrypt(nonce, data, None)
88
+
89
+ @staticmethod
90
+ async def create(keypath: str) -> 'EncryptionCodec':
91
+ keys = List[Key]
92
+ with open(keypath) as f:
93
+ data = yaml.safe_load(f)
94
+
95
+ keys: List[Key] = [Key(**item) for item in data]
96
+
97
+ return EncryptionCodec(keys)
File without changes
@@ -0,0 +1,8 @@
1
+ version = 1
2
+ revision = 2
3
+ requires-python = ">=3.9"
4
+
5
+ [[package]]
6
+ name = "temporalcodec"
7
+ version = "0.0.1"
8
+ source = { editable = "." }