sovereign 1.0.0a4__py3-none-any.whl → 1.0.0b148__py3-none-any.whl

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.

Potentially problematic release.


This version of sovereign might be problematic. Click here for more details.

Files changed (65) hide show
  1. sovereign/__init__.py +2 -2
  2. sovereign/app.py +3 -6
  3. sovereign/cache/__init__.py +12 -85
  4. sovereign/cache/backends/__init__.py +1 -1
  5. sovereign/cache/backends/s3.py +6 -24
  6. sovereign/cache/filesystem.py +5 -6
  7. sovereign/cache/types.py +0 -2
  8. sovereign/configuration.py +8 -42
  9. sovereign/context.py +9 -8
  10. sovereign/dynamic_config/__init__.py +4 -3
  11. sovereign/dynamic_config/deser.py +1 -1
  12. sovereign/dynamic_config/loaders.py +3 -3
  13. sovereign/error_info.py +3 -2
  14. sovereign/events.py +3 -3
  15. sovereign/logging/access_logger.py +1 -1
  16. sovereign/logging/application_logger.py +1 -1
  17. sovereign/logging/bootstrapper.py +1 -1
  18. sovereign/modifiers/lib.py +1 -1
  19. sovereign/rendering.py +90 -22
  20. sovereign/response_class.py +2 -2
  21. sovereign/server.py +26 -45
  22. sovereign/sources/__init__.py +3 -0
  23. sovereign/sources/file.py +21 -0
  24. sovereign/sources/inline.py +39 -0
  25. sovereign/sources/lib.py +41 -0
  26. sovereign/sources/poller.py +537 -0
  27. sovereign/statistics.py +1 -2
  28. sovereign/testing/loaders.py +0 -1
  29. sovereign/tracing.py +5 -6
  30. sovereign/types.py +10 -15
  31. sovereign/utils/auth.py +2 -3
  32. sovereign/utils/crypto/suites/disabled_cipher.py +2 -2
  33. sovereign/utils/dictupdate.py +1 -1
  34. sovereign/utils/eds.py +1 -3
  35. sovereign/utils/entry_point_loader.py +2 -2
  36. sovereign/utils/mock.py +3 -4
  37. sovereign/utils/resources.py +1 -1
  38. sovereign/utils/templates.py +2 -4
  39. sovereign/utils/timer.py +3 -5
  40. sovereign/utils/weighted_clusters.py +1 -2
  41. sovereign/views/__init__.py +3 -6
  42. sovereign/views/api.py +7 -28
  43. sovereign/views/crypto.py +1 -1
  44. sovereign/views/discovery.py +5 -20
  45. sovereign/views/healthchecks.py +27 -45
  46. sovereign/views/interface.py +10 -70
  47. sovereign/worker.py +31 -20
  48. {sovereign-1.0.0a4.dist-info → sovereign-1.0.0b148.dist-info}/METADATA +3 -4
  49. sovereign-1.0.0b148.dist-info/RECORD +77 -0
  50. {sovereign-1.0.0a4.dist-info → sovereign-1.0.0b148.dist-info}/entry_points.txt +0 -8
  51. sovereign/rendering_common.py +0 -91
  52. sovereign/v2/__init__.py +0 -0
  53. sovereign/v2/data/data_store.py +0 -621
  54. sovereign/v2/data/render_discovery_response.py +0 -24
  55. sovereign/v2/data/repositories.py +0 -90
  56. sovereign/v2/data/utils.py +0 -33
  57. sovereign/v2/data/worker_queue.py +0 -273
  58. sovereign/v2/jobs/refresh_context.py +0 -117
  59. sovereign/v2/jobs/render_discovery_job.py +0 -145
  60. sovereign/v2/logging.py +0 -81
  61. sovereign/v2/types.py +0 -41
  62. sovereign/v2/web.py +0 -101
  63. sovereign/v2/worker.py +0 -199
  64. sovereign-1.0.0a4.dist-info/RECORD +0 -85
  65. {sovereign-1.0.0a4.dist-info → sovereign-1.0.0b148.dist-info}/WHEEL +0 -0
sovereign/v2/logging.py DELETED
@@ -1,81 +0,0 @@
1
- import logging
2
- import os
3
- from typing import Any, MutableMapping
4
-
5
- import structlog
6
- from pydantic import BaseModel
7
-
8
- # noinspection PyProtectedMember
9
- from structlog.dev import RichTracebackFormatter
10
- from structlog.typing import FilteringBoundLogger
11
-
12
-
13
- def get_named_logger(name: str, level: int = logging.INFO) -> FilteringBoundLogger:
14
- """
15
- Gets a structured logger with a specific name to allow us to control log levels separately.
16
-
17
- Set LOG_FORMAT=human for pretty-printed, colourful output.
18
- """
19
-
20
- # noinspection PyUnusedLocal
21
- def filter_by_level(
22
- logger: Any, method_name: str, event_dict: MutableMapping[str, Any]
23
- ) -> MutableMapping[str, Any]:
24
- level_map = {
25
- "debug": logging.DEBUG,
26
- "info": logging.INFO,
27
- "warn": logging.WARN,
28
- "warning": logging.WARNING,
29
- "error": logging.ERROR,
30
- "critical": logging.CRITICAL,
31
- "exception": logging.ERROR,
32
- }
33
- method_level = level_map.get(method_name, logging.INFO)
34
- if method_level < level:
35
- raise structlog.DropEvent
36
- return event_dict
37
-
38
- # noinspection PyUnusedLocal
39
- def serialise_pydantic_models(
40
- logger: FilteringBoundLogger,
41
- method_name: str,
42
- event_dict: MutableMapping[str, Any],
43
- ) -> MutableMapping[str, Any]:
44
- for key, value in event_dict.items():
45
- if isinstance(value, BaseModel):
46
- event_dict[key] = value.model_dump()
47
- return event_dict
48
-
49
- log_format = os.environ.get("LOG_FORMAT", "json").lower()
50
- is_human_format = log_format == "human"
51
-
52
- base_processors = [
53
- filter_by_level,
54
- structlog.stdlib.add_log_level,
55
- serialise_pydantic_models,
56
- structlog.processors.TimeStamper(
57
- fmt="iso" if not is_human_format else "%Y-%m-%d %H:%M:%S"
58
- ),
59
- ]
60
-
61
- if is_human_format:
62
- # human-readable format with colours
63
- final_processors = base_processors + [
64
- structlog.processors.UnicodeDecoder(),
65
- structlog.dev.ConsoleRenderer(
66
- colors=True,
67
- exception_formatter=RichTracebackFormatter(show_locals=False),
68
- pad_event=30,
69
- sort_keys=False,
70
- ),
71
- ]
72
- else:
73
- # JSON format for production/machine consumption
74
- current_processors = list(structlog.get_config()["processors"])
75
- final_processors = base_processors + current_processors
76
-
77
- return structlog.wrap_logger(
78
- structlog.PrintLogger(),
79
- final_processors,
80
- context_class=dict,
81
- ).bind(logger_name=name)
sovereign/v2/types.py DELETED
@@ -1,41 +0,0 @@
1
- from typing import Any
2
-
3
- import pydantic
4
- from pydantic import TypeAdapter
5
-
6
- from sovereign.types import DiscoveryRequest, DiscoveryResponse
7
-
8
-
9
- class Context(pydantic.BaseModel):
10
- name: str
11
- data: Any
12
- data_hash: int
13
- last_refreshed_at: int | None = None
14
- refresh_after: int
15
-
16
-
17
- class DiscoveryEntry(pydantic.BaseModel):
18
- request_hash: str
19
- template: str
20
- request: DiscoveryRequest
21
- response: DiscoveryResponse | None
22
- last_rendered_at: int | None = None
23
-
24
-
25
- class RefreshContextJob(pydantic.BaseModel):
26
- context_name: str
27
-
28
-
29
- class RenderDiscoveryJob(pydantic.BaseModel):
30
- request_hash: str
31
-
32
-
33
- QueueJob = RefreshContextJob | RenderDiscoveryJob
34
-
35
-
36
- queue_job_type_adapter = TypeAdapter(QueueJob)
37
-
38
-
39
- class WorkerNode(pydantic.BaseModel):
40
- node_id: str
41
- last_heartbeat: int
sovereign/v2/web.py DELETED
@@ -1,101 +0,0 @@
1
- import asyncio
2
- import logging
3
- import os
4
- import threading
5
-
6
- from structlog.typing import FilteringBoundLogger
7
-
8
- from sovereign import config
9
- from sovereign.types import DiscoveryRequest, DiscoveryResponse
10
- from sovereign.v2.data.repositories import DiscoveryEntryRepository
11
- from sovereign.v2.data.utils import get_data_store, get_queue
12
- from sovereign.v2.logging import get_named_logger
13
- from sovereign.v2.types import DiscoveryEntry, RenderDiscoveryJob
14
-
15
-
16
- async def wait_for_discovery_response(
17
- request: DiscoveryRequest,
18
- ) -> DiscoveryResponse | None:
19
- # 1 - check if the entry already exists in the database with a non-empty response
20
- # 2 - if it does, return it
21
- # 3 - if it doesn't, enqueue a new job to render it
22
- # 4 - poll for up to CACHE_READ_TIMEOUT seconds, if we find a response, return it
23
-
24
- request_hash = request.cache_key(config.cache.hash_rules)
25
-
26
- logger: FilteringBoundLogger = get_named_logger(
27
- f"{__name__}.{wait_for_discovery_response.__qualname__} ({__file__})",
28
- level=logging.DEBUG,
29
- ).bind(
30
- request_hash=request_hash,
31
- template_resource_type=request.template.resource_type,
32
- process_id=os.getpid(),
33
- thread_id=threading.get_ident(),
34
- )
35
-
36
- logger.debug("Starting lookup for discovery response")
37
-
38
- data_store = get_data_store()
39
- discovery_entry_repository = DiscoveryEntryRepository(data_store)
40
-
41
- queue = get_queue()
42
-
43
- discovery_entry = discovery_entry_repository.get(request_hash)
44
-
45
- if not discovery_entry:
46
- logger.debug(
47
- "No existing discovery entry found, creating new entry and enqueuing job"
48
- )
49
-
50
- # we need to save this request to the database
51
- discovery_entry = DiscoveryEntry(
52
- request_hash=request_hash,
53
- template=request.template.resource_type,
54
- request=request,
55
- response=None,
56
- )
57
- discovery_entry_repository.save(discovery_entry)
58
-
59
- if not discovery_entry.response:
60
- # enqueue a job to render this discovery request (duplicates handled in the worker)
61
- job = RenderDiscoveryJob(request_hash=request_hash)
62
- queue.put(job)
63
-
64
- if discovery_entry.response:
65
- logger.debug("Returning cached response immediately")
66
- return discovery_entry.response
67
-
68
- # wait for up to CACHE_READ_TIMEOUT seconds for the response to be populated
69
- logger.debug(
70
- "Polling for response",
71
- timeout=config.cache.read_timeout,
72
- poll_interval=config.cache.poll_interval_secs,
73
- )
74
-
75
- start_time = asyncio.get_event_loop().time()
76
- attempts = 0
77
-
78
- while (
79
- asyncio.get_event_loop().time() - start_time
80
- ) < config.cache.read_timeout and discovery_entry.response is None:
81
- attempts += 1
82
- discovery_entry = discovery_entry_repository.get(request_hash)
83
- if discovery_entry is None:
84
- logger.error("No discovery entry found while polling for response")
85
- return None
86
- await asyncio.sleep(config.cache.poll_interval_secs)
87
-
88
- elapsed_time = asyncio.get_event_loop().time() - start_time
89
-
90
- if discovery_entry.response:
91
- logger.debug(
92
- "Response received after polling",
93
- attempts=attempts,
94
- elapsed_time=elapsed_time,
95
- )
96
- else:
97
- logger.error(
98
- "Timeout waiting for response", attempts=attempts, elapsed_time=elapsed_time
99
- )
100
-
101
- return discovery_entry.response
sovereign/v2/worker.py DELETED
@@ -1,199 +0,0 @@
1
- import logging
2
- import os
3
- import random
4
- import threading
5
- import time
6
-
7
- from structlog.typing import FilteringBoundLogger
8
-
9
- from sovereign import stats
10
- from sovereign.configuration import config
11
- from sovereign.dynamic_config import Loadable
12
- from sovereign.v2.data.data_store import DataStoreProtocol
13
- from sovereign.v2.data.repositories import (
14
- ContextRepository,
15
- DiscoveryEntryRepository,
16
- WorkerNodeRepository,
17
- )
18
- from sovereign.v2.data.utils import get_data_store, get_queue
19
- from sovereign.v2.data.worker_queue import QueueProtocol
20
- from sovereign.v2.jobs.refresh_context import get_refresh_after, refresh_context
21
- from sovereign.v2.jobs.render_discovery_job import render_discovery_response
22
- from sovereign.v2.logging import get_named_logger
23
- from sovereign.v2.types import (
24
- QueueJob,
25
- RefreshContextJob,
26
- RenderDiscoveryJob,
27
- )
28
-
29
-
30
- class Worker:
31
- context_repository: ContextRepository
32
- discovery_entry_repository: DiscoveryEntryRepository
33
- worker_node_repository: WorkerNodeRepository
34
-
35
- queue: QueueProtocol
36
-
37
- def __init__(
38
- self,
39
- data_store: DataStoreProtocol | None = None,
40
- node_id: str | None = None,
41
- queue: QueueProtocol | None = None,
42
- ) -> None:
43
- self.logger: FilteringBoundLogger = get_named_logger(
44
- f"{self.__class__.__module__}.{self.__class__.__qualname__}",
45
- level=logging.INFO,
46
- )
47
-
48
- self.node_id = (
49
- node_id
50
- if node_id is not None
51
- else f"{time.time()}{random.randint(0, 1000000)}"
52
- )
53
-
54
- data_store = data_store if data_store is not None else get_data_store()
55
-
56
- self.context_repository = ContextRepository(data_store)
57
- self.discovery_entry_repository = DiscoveryEntryRepository(data_store)
58
- self.worker_node_repository = WorkerNodeRepository(data_store)
59
-
60
- self.queue = queue if queue is not None else get_queue()
61
-
62
- def start(self):
63
- # start the context refresh loop and daemonise it
64
- threading.Thread(daemon=True, target=self.context_refresh_loop).start()
65
-
66
- # pull from the queue for eternity and process the messages
67
- while True:
68
- try:
69
- if message := self.queue.get():
70
- job_type = type(message.job).__name__
71
- stats.increment(
72
- "v2.worker.queue.message_received",
73
- tags=[f"job_type:{job_type}"],
74
- )
75
- self.process_job(message.job)
76
- self.queue.ack(message.receipt_handle)
77
- stats.increment(
78
- "v2.worker.queue.message_acked", tags=[f"job_type:{job_type}"]
79
- )
80
- except Exception:
81
- stats.increment("v2.worker.queue.error")
82
- self.logger.exception("Error while processing job")
83
-
84
- def process_job(self, job: QueueJob):
85
- self.logger.info(
86
- "Processing job from queue",
87
- job_type=type(job),
88
- job=job,
89
- node_id=self.node_id,
90
- process_id=os.getpid(),
91
- thread_id=threading.get_ident(),
92
- )
93
-
94
- match job:
95
- case RefreshContextJob():
96
- refresh_context(
97
- job.context_name,
98
- self.node_id,
99
- config,
100
- self.context_repository,
101
- self.discovery_entry_repository,
102
- self.queue,
103
- )
104
- case RenderDiscoveryJob():
105
- render_discovery_response(
106
- job.request_hash,
107
- self.context_repository,
108
- self.discovery_entry_repository,
109
- self.node_id,
110
- )
111
-
112
- def context_refresh_loop(self):
113
- self.logger.info(
114
- "Starting context refresh loop",
115
- node_id=self.node_id,
116
- process_id=os.getpid(),
117
- thread_id=threading.get_ident(),
118
- )
119
-
120
- is_leader = False
121
-
122
- while True:
123
- try:
124
- self.worker_node_repository.send_heartbeat(self.node_id)
125
- self.worker_node_repository.prune_dead_nodes()
126
-
127
- if not self.worker_node_repository.get_leader_node_id() == self.node_id:
128
- is_leader = False
129
- self.logger.info(
130
- "This node is not the leader, checking again in 60 seconds",
131
- node_id=self.node_id,
132
- process_id=os.getpid(),
133
- thread_id=threading.get_ident(),
134
- )
135
- time.sleep(60)
136
- continue
137
-
138
- # I am the leader
139
- if not is_leader:
140
- is_leader = True
141
- self.logger.info(
142
- "This node is the leader, begin refreshing contexts",
143
- node_id=self.node_id,
144
- process_id=os.getpid(),
145
- thread_id=threading.get_ident(),
146
- )
147
-
148
- name: str
149
- loadable: Loadable
150
- for name, loadable in config.template_context.context.items():
151
- refresh_after: int | None = (
152
- self.context_repository.get_refresh_after(name)
153
- )
154
-
155
- time_now = time.time()
156
-
157
- if refresh_after is None or refresh_after <= time.time():
158
- job = RefreshContextJob(context_name=name)
159
-
160
- self.logger.info(
161
- "Queuing context refresh",
162
- node_id=self.node_id,
163
- process_id=os.getpid(),
164
- thread_id=threading.get_ident(),
165
- name=name,
166
- refresh_after=refresh_after,
167
- refresh_after_seconds=(refresh_after or time_now)
168
- - time_now,
169
- )
170
- self.queue.put(job)
171
- stats.increment(
172
- "v2.worker.context_refresh.queued", tags=[f"context:{name}"]
173
- )
174
-
175
- # update refresh_after to ensure that, at most, we refresh once per interval
176
- self.context_repository.update_refresh_after(
177
- name, get_refresh_after(config, loadable)
178
- )
179
- else:
180
- stats.increment(
181
- "v2.worker.context_refresh.skipped",
182
- tags=[f"context:{name}"],
183
- )
184
- self.logger.debug(
185
- "Skipping context refresh",
186
- node_id=self.node_id,
187
- process_id=os.getpid(),
188
- thread_id=threading.get_ident(),
189
- name=name,
190
- refresh_after=refresh_after,
191
- refresh_after_seconds=(refresh_after or time_now)
192
- - time_now,
193
- )
194
-
195
- time.sleep(1)
196
- except Exception:
197
- stats.increment("v2.worker.context_refresh.error")
198
- self.logger.exception("Error while refreshing context")
199
- time.sleep(5)
@@ -1,85 +0,0 @@
1
- sovereign/__init__.py,sha256=D6-BdAO7EyeQ7KU3sl9oiBYsGXDBkc5UM7I1kkas1Eg,1069
2
- sovereign/app.py,sha256=l7AO8PtHsiVIN6ybWKj0qgzPJ-cNX8q9_syLfIG9XHY,5117
3
- sovereign/configuration.py,sha256=AqDOVc7eOgEsP8BOvW5UsMhbC2j2FR72Uios76gK6Ao,21177
4
- sovereign/constants.py,sha256=qdWD1lTvkaW5JGF7TmZhfksQHlRAJFVqbG7v6JQA9k8,46
5
- sovereign/context.py,sha256=nwLJ_gvXFoKZJMCt4NX3KRkM381XMq_DJfTedceToJQ,9076
6
- sovereign/error_info.py,sha256=_xfwYCV10zpAa0XP_zz0FoGtjj-1S_f4IgOf78-6wcw,1487
7
- sovereign/events.py,sha256=gSWxaEqRdYfA_y7iFEzQPPrg3N5jy9svpfHzn6kBdRk,1203
8
- sovereign/middlewares.py,sha256=6w4JpvtNGvQA4rocQsYQjuu-ckhpKT6gKYA16T-kiqA,3082
9
- sovereign/rendering.py,sha256=qKtO0BUoqRs_FqDGsw2o0_756Uj59aBhfeKRpNjFlQM,4103
10
- sovereign/rendering_common.py,sha256=WvZ_vCHD3ODiAX3Hw6Q6GwTklH4a2BFY3-zwqDNnmjg,3395
11
- sovereign/response_class.py,sha256=S-8xziWdWVk8jbde_2OdxV1XWdrw6EwInLLhFmaYgkA,427
12
- sovereign/server.py,sha256=5JQJ72yyOA4rjVs4rFznwYvh7zFEsg48cTDBqCvmsQo,3650
13
- sovereign/statistics.py,sha256=0bbL7eLGOC-_hOY_ilx2uV_VpSJTIYjBGvEztDlgQDs,2045
14
- sovereign/tracing.py,sha256=xL1blm4u2gmahpkFKhwY-pA-hE976rDXi9QxaOdRnoI,2686
15
- sovereign/types.py,sha256=j8w3tXfkm0FHcMP8WXcXd99D1YhJ-cIuc-oVV0LuBSc,10245
16
- sovereign/worker.py,sha256=ecLm_LDnbJPVzQPLmBhF3J-cWpzPKo6H2HAncDNbneE,5615
17
- sovereign/cache/__init__.py,sha256=bq5BPiO-oCpSmiQ7GoXDk3PckVqYNS2NADguWBcRzeQ,9536
18
- sovereign/cache/filesystem.py,sha256=GQP34jhJP2JsaEQgnI4O80EuN6sdK7lnKbP37jrI4d8,2416
19
- sovereign/cache/types.py,sha256=JOLrPI8X209Fndt5xtLtEWMB6Fh7cvnzwsEBAoBV06c,243
20
- sovereign/cache/backends/__init__.py,sha256=3oKlNWuHd5KyETor1napzq6nIJv0KGbOEPUrIZf35So,3160
21
- sovereign/cache/backends/s3.py,sha256=92YPCBW9t4wLNQuCiES29GJ36lUP1IWIJDMHWFGNJDA,5865
22
- sovereign/dynamic_config/__init__.py,sha256=VU71zUxc6zwioX3zm9VTzj_XQl8ye6FJGPuna3BaieY,3546
23
- sovereign/dynamic_config/deser.py,sha256=uJsv6vWEPb-dJ9PIDZ51J-KC8J-IikbTvUM8hmvKAyY,1834
24
- sovereign/dynamic_config/loaders.py,sha256=hp8drF4jXaotO-7CGudgs_enkg63vrXW34nTzP4MHlY,2915
25
- sovereign/logging/access_logger.py,sha256=W5GE0fOjkYTwdA8KVsbPzof_FcPfJ8kCOpnL_tEi9gw,2989
26
- sovereign/logging/application_logger.py,sha256=DKrLVX8sl7ckGYCUlSJLwJ9K9nomNoLBitzhEj2s_K8,1806
27
- sovereign/logging/base_logger.py,sha256=ScOzHs8Rt1RZaUZGvaJSAlDEjD0BxkD5sLKSm2GgM0I,1243
28
- sovereign/logging/bootstrapper.py,sha256=9a13x8gDEH0fiDkpEkwxmQYzWmb8jqPJKx4QCgdT7II,1302
29
- sovereign/logging/types.py,sha256=rGqJAEVvgvzHy4aPfvEH6yQ-yblXNkEcWG7G8l9ALEA,282
30
- sovereign/modifiers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- sovereign/modifiers/lib.py,sha256=tck5rw6PNqLofBtm3GTRMmrfrV9eSj_Rwhn5tM3Q2UA,2885
32
- sovereign/templates/base.html,sha256=MMhhvvClTixKibYfhXm8Ezx6ttu6Sqki44niciCPMO4,2990
33
- sovereign/templates/err.html,sha256=a3cEzOqyqWOIe3YxfTEjkxbTfxBxq1knD6GwzEFljfs,603
34
- sovereign/templates/resources.html,sha256=5MfXHW8s3tAWda66Q48zVgDhZNLwHGsdCKkKHLZohIs,10420
35
- sovereign/testing/loaders.py,sha256=JEjoxPSy0O-SUowW1U7P_BX6ABuqrmUrCuAxsUZaMos,200
36
- sovereign/testing/modifiers.py,sha256=YI0aJOEbb1C_1T5VDrZKyLopk8JiIlKtUtxXMFfsCtk,290
37
- sovereign/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
- sovereign/utils/auth.py,sha256=e9lMNo8KqScTBITz4upQ0qL8A-4DTl-ybCy9DPwpfoM,2307
39
- sovereign/utils/dictupdate.py,sha256=R60mHb3qf4r-KT-SyxQJv7KQktmtzp-bRtVOjebo0To,2558
40
- sovereign/utils/eds.py,sha256=eykFVKaLXoMeieJ2TnArx3q5KIJeQYiB-OYQrTWtslw,4391
41
- sovereign/utils/entry_point_loader.py,sha256=OPvaLfwUH0XGYw9WM3hdi_-uBAwmibBoq2NEjLFtnKs,526
42
- sovereign/utils/mock.py,sha256=Oc5p8conSku2t6b75m7nMHsESepLmnv_itHcAE8cIXw,2396
43
- sovereign/utils/resources.py,sha256=otKaJZIDQa4So_cKkFN9gBsLZaG6rH0hg9hjELn1Qic,472
44
- sovereign/utils/templates.py,sha256=yML5rU-BUY-2oQx6gOK7fOkLjtD5VSmaH8vzPKnetSc,1059
45
- sovereign/utils/timer.py,sha256=DvbjDOSXZsGXigWpuRxE1NyVrpq4qktIRQhJIOt2Dcs,919
46
- sovereign/utils/version_info.py,sha256=adBfu0z6jsg8E5-BIUjZyBwZvfLASj7fpCpYeIvBeMY,576
47
- sovereign/utils/weighted_clusters.py,sha256=RTRLX5TFvL426_B-M8XhC7FeGelHzpgGpHwQaVFEHRQ,1167
48
- sovereign/utils/crypto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
- sovereign/utils/crypto/crypto.py,sha256=FMo4TikqWcPJ5fyc6j2WEHkkwZU3WY3qJKr7VLfckv0,4532
50
- sovereign/utils/crypto/suites/__init__.py,sha256=smMvNa1VsQ0PvsNj6lnRNh4ktB7dMnas1CqeTOFqgGA,526
51
- sovereign/utils/crypto/suites/aes_gcm_cipher.py,sha256=Yjfj1LCQDGTzHBjrZR3-koh29L_N34v65kPoIfta0aw,1239
52
- sovereign/utils/crypto/suites/base_cipher.py,sha256=kUOZh_ZIILyo5zv99-qzbJZDpeMmt76vhkBDEPvAt4A,454
53
- sovereign/utils/crypto/suites/disabled_cipher.py,sha256=3uagd2IkE-IHLkfOgX1rofatHIBVLryV_hGvdJPmuTM,579
54
- sovereign/utils/crypto/suites/fernet_cipher.py,sha256=rP6M5ys1vctyadOxDGNFoyerWPUOunLQdZ2jjS1pxzc,701
55
- sovereign/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
- sovereign/v2/logging.py,sha256=ia8ntWngTKXlMzFhkVNScIDaFTrL0HkIkUT-afQGEJE,2639
57
- sovereign/v2/types.py,sha256=0qclM0aylR1-FjCVCzlYlZShNICWt6wI-52B3SkSC1w,800
58
- sovereign/v2/web.py,sha256=dzjLJ0lXdGZqkGc237QHVe930Hmls9m_e-clas9mOps,3443
59
- sovereign/v2/worker.py,sha256=rGqDsinDCtGNbCmBDF1mHIik4KJH0rKpH4WLzL-VDAo,7467
60
- sovereign/v2/data/data_store.py,sha256=IDjV3ouYjVxWdcvcAUIPrfnXVwAbbyMmkbXjdfKfnw8,20310
61
- sovereign/v2/data/render_discovery_response.py,sha256=3AFMXBu1aBRaSLjUTxVUI9Lt-to0B5eXY5m1SMO7dB8,949
62
- sovereign/v2/data/repositories.py,sha256=KsdFvOIDZmkZDe-SmatX4GialhRk4LWD_2pOBeOTAQs,3292
63
- sovereign/v2/data/utils.py,sha256=cJ93ioHXPHquMhqvYY4SulbWVgTPxY6t_gvYhOt6T1s,1075
64
- sovereign/v2/data/worker_queue.py,sha256=aDTLqeXM-qx-CJ0VpOaNVKkGmlEllVQxERXIaS0Kv9w,10407
65
- sovereign/v2/jobs/refresh_context.py,sha256=lhzP9zl8U4fA0ND-SFJs4HefnEAtv8d7no4u5cFI-L4,4277
66
- sovereign/v2/jobs/render_discovery_job.py,sha256=Z5L4Qj54tg8FVlaMaBBCZiJgAqJIS-Rl28ekxoJ1o58,5451
67
- sovereign/views/__init__.py,sha256=0niB6LnhAZQzI4nk_xXsWk95Idm0YFINLoL8WhNdA70,183
68
- sovereign/views/api.py,sha256=dcNy9HKEB9n6DAQI_gP5cyVLn_4YFOODMx1zIu2-J6Y,2954
69
- sovereign/views/crypto.py,sha256=0UX0mEGQmiwRcR8gMeIkNYY2ykEGpPC09xmfFRsh-bY,3366
70
- sovereign/views/discovery.py,sha256=1yIKSIuVnPT7wtPd0fJ7KdEp5ZZf8PdWP67XoI_4R6s,3111
71
- sovereign/views/healthchecks.py,sha256=56exKqRz4Qdz3KJlbwHWZNIQ-yJUOgpDnzROrzQn3rg,4108
72
- sovereign/views/interface.py,sha256=vHTdboqhBMGyX4COnExKbal51hOXFX4txaK0nmz8bs8,9294
73
- sovereign_files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
- sovereign_files/static/darkmode.js,sha256=3ip-eKGctDvNhN7UgmaHhzls7r5qIY-Jvh2EpefHbQ0,1449
75
- sovereign_files/static/node_expression.js,sha256=GKxKTSRc_96IbL3H4L_31ueJFXq4N7scm5R1RNqxP24,1489
76
- sovereign_files/static/panel.js,sha256=i5mGExjv-I4Gtt9dQiTyFwPZa8pg5rXeuTeidXNUiTE,2695
77
- sovereign_files/static/resources.css,sha256=Rt_ir_FkoI-VIAOqPhk0vILy8kB2egAYbQU26SOs1io,4500
78
- sovereign_files/static/resources.js,sha256=-TaXZ6tohyKA1SkX5YwrTcV5M8mOZ68cvEXpvZWznTo,24506
79
- sovereign_files/static/style.css,sha256=kmvkJ2820RKehWxhddkucbgFkvnpUgBMteOtpEuXjvQ,601347
80
- sovereign_files/static/style.css.map,sha256=h1ufjfDVX-8z-FuJqFG2-U9AVdi66U-e8uyiGdUZjDw,66576
81
- sovereign_files/static/sass/style.scss,sha256=LdGXXuHi_tyMc7XhijIOrlIxyfLt827AAs2Z7DYpFpg,990
82
- sovereign-1.0.0a4.dist-info/METADATA,sha256=i7YfqN62JPqLC2-55IdBfaXkhZaajFwufE6XG6glcG0,5943
83
- sovereign-1.0.0a4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
84
- sovereign-1.0.0a4.dist-info/entry_points.txt,sha256=Mj1IvDg3Y61lasbqkGtEWpPn_xgYWCyeuMnOl8hTLVQ,1787
85
- sovereign-1.0.0a4.dist-info/RECORD,,